In-N-Out Animations: Popovers (Part 2/3)

Chris Coyier Chris Coyier on

We kicked this series off looking a animating in and out the <dialog> element. This time we’re going to look at popovers. That is, this modern beauty:

<button popovertarget="my-popover">
  Toggle Popover
</button>

<aside popover id="my-popover">
  Content of popover
</aside>Code language: HTML, XML (xml)

That “popover” will open and close (i.e. change from display: none; to display: block;) automatically with the button press. But we want to animate that! In and out!

Admittedly, popovers are fairly similar to dialogs (but certainly have important differences), and that is part of the point. We’re going to show off that our three-phase system we established in the first article transfers over to something else just fine.

Article Series

Demo so you an see what we’re doing:

Reminder: The 3-2-1 System

The important thing to remember about styling both the in and out of an element was styling all three states and in order such that the “on the way in” styles have enough specificity to win over the open styles.

It’s essentially like this

.element {
  transition: ...;

  /* 3 */
  &:not(.open) {

  }

  /* 2 */
  &.open {
  
  }
  
  /* 1 */
  @starting-style {
    &.open {

    }
  }
}Code language: CSS (css)

3-2-1 with Popovers

Popovers have their own special pseudo-class for determining if they are open or not, so we can apply it like like below. This time, we’ll transition the opacity and rotate property. Anything is on the table! That’s just what we’re choosing this time.

[popover] {
  --timing: .66s;
  
  transition:
    var(--timing) opacity,
    var(--timing) rotate,
    var(--timing) display allow-discrete,
    var(--timing) overlay allow-discrete;

  /* 3 */
  &:not(:popover-open) {
    opacity: 0;
    rotate: 10deg;
    transform-origin: top left;
  }

  /* 2 */
  &:popover-open {
    --timing: 0.2s;
    opacity: 1;
    rotate: 3deg;
  }

  /* 1 */
  @starting-style {
    &:popover-open {
      opacity: 0;
      rotate: -3deg;
    }
  }
}Code language: JavaScript (javascript)

Handling Reduced Motion

We’re introducing movement on the page here. We should take care to not do that if the user doesn’t want it. But the opacity change is fine. So we’d do it like this.

@media (prefers-reduced-motion: reduce) {
  [popover] {  
    transition:
      var(--timing) opacity,
      /* rotate is removed! */
      var(--timing) display allow-discrete,
      var(--timing) overlay allow-discrete;

    &, &:popover-open {
      rotate: 0deg;
    }
  }
}Code language: JavaScript (javascript)

Note that we’ve made the popover have no rotation. That’s not a requirement, you could leave it tilted if you want.

Here’s the final demo.

Noting a Safari Quirk

Positioning anchors for popovers are “implied” in most browsers. And that’s true of Safari as well. You can see when we open the popover, it’s positioned where the button that opened it is. But “on the way out” it loses that anchor for some reason. Bug? Unsure. So in the demo we’re just being specific about the anchor and that fixes it.

Also, in Part 1, the “on the way out” styles for the <dialog> have an issue with the inset property, so we set it explicitly to inset: 0; and that fixes Safari.

Article Series

Wanna learn CSS Animations deeply?

Leave a Reply

Your email address will not be published. Required fields are marked *

$966,000

Frontend Masters donates to open source projects through thanks.dev and Open Collective, as well as donates to non-profits like The Last Mile, Annie Canons, and Vets Who Code.