10 Dropdown Menu Animations with CSS Transforms
Tags: css, codepen, webdev
Published: 2020-03-19 15:00:06


*This was originally published on [codinhood.com](https://codinhood.com/micro/10-dropdown-menu-animations-css-transform)*

Dropdown menus allow the user to see more information about a particular topic without overwhelming the page with too much information. Most dropdown menu animations follow one of two patterns, animating the whole menu as a group or animating each item within the menu individually. CSS transforms allow us to animate either pattern with great performance because CSS transforms [affect the Composite step in your browser's Critical Rendering Path](https://medium.com/outsystems-experts/how-to-achieve-60-fps-animations-with-css3-db7b98610108) rather than the Paint, Layout, or Styles step. Below are five animations using CSS transforms for both the whole menu and each item individually. The CSS syntax is [Stylus](https://stylus-lang.com/), which is a similar to [SASS](https://sass-lang.com/).

While animations can add some fun visual flair, keep in mind these examples are slightly exaggerated to make the mechanics of the animation more obvious.

{% codepen https://codepen.io/codypearce/pen/PdBXpj %}

## Animating the Whole Dropdown Menu

The structure is the same for all menus: `dropdown` is the container for the menu title and the hidden menu. `dropdown_menu` is the actual menu itself, which is by default hidden.

```html
<li class="dropdown dropdown-6">
  Scale Down
  <ul class="dropdown_menu dropdown_menu--animated dropdown_menu-6">
    <li class="dropdown_item-1">Item 1</li>
    <li class="dropdown_item-2">Item 2</li>
    <li class="dropdown_item-3">Item 3</li>
    <li class="dropdown_item-4">Item 4</li>
    <li class="dropdown_item-5">Item 5</li>
  </ul>
</li>
```

The `dropdown_menu` is displayed only when the whole dropdown (the title and button) is hovered. This will display the dropdown menu on hover without any animations.

```sass
.dropdown_menu
  position: absolute
  top: 100%
  left: 0
  width: 100%
  perspective: 1000px
  display: none
.dropdown:hover .dropdown_menu--animated
  display: block
```

To animate the menu we need to add the [`animation`](https://developer.mozilla.org/en-US/docs/Web/CSS/animation) property to the `dropdown_menu`. The `animation-name` property should be set the name we give to the `@keyframes` rule, in this case, `growDown`. The [`animation-fill-mode`](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-fill-mode) property defines what styles should be applied at the end of the animation. In our case, we are animating the menu from the closed state to the open state. This means we want the dropdown menu to preserve the styles of the final animation state, the value `forwards` allows us to do that.

```sass
.dropdown_menu-6
  animation: growDown 300ms ease-in-out forwards
```

The last property we need to consider is [transform-origin](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin), which defines where the animation is applied. For our animations, this property defines where the menu originates from physically. The default setting of `transform-origin` will scale the menu from the "center of the menu" not from the button. For example, animating the `roateX` transform property will look like this with the default `transform-origin` value.

![Transform Origin Center](https://codinhood.com/ba4c2453bfa4ea406e725de4736823e7/transform-origin-center.gif)

Changing `transform-origin` to `top center` will make the animation rotate from the button.

```sass
.dropdown_menu-6
  animation: growDown 300ms ease-in-out forwards
  transform-origin: top center
```

![Transform Origin Top](https://codinhood.com/0eeb0e50880952aa3c6e9552bd43685b/transform-origin-top.gif)

We can choose from any of the [21 transform functions](https://developer.mozilla.org/en-US/docs/Web/CSS/transform) to animate our menu using a [@keyframes](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes) rule. The only rule we need to follow is that the final state of the animation, the `100%` property, must be the normal state for the menu being open. For example, if we animate the `scaleY` of the menu, we need to make sure that `scaleY` is set to `1` so the menu looks normal when the animation is finished.

You'll notice that each animation "goes past" the intended end state around `80%` or `70%` then returns to the end state at `100%`. This is a common technique in animation to create the illusion of momentum or "bounce" to give it more of a natural feel. Two examples are shown below, but check the Codepen above for more examples.

```sass

@keyframes growDown {
  0% {
    transform: scaleY(0)
  }
  80% {
    transform: scaleY(1.1)
  }
  100% {
    transform: scaleY(1)
  }
}
```

```sass
@keyframes rotateMenu {
  0% {
    transform: rotateX(-90deg)
  }
  70% {
    transform: rotateX(20deg)
  }
  100% {
    transform: rotateX(0deg)
  }
}
```

## Animating Each Menu Item

The HTML structure is identical to animating the whole menu. Each menu item, however, needs to set `opacity` to `0` in addition to setting `display: none`. We do this is because we don't want all menu items to be visible immediately, like we did when animating the whole menu. Instead, we will animate each into view individually by animating opacity with a transform.

```sass
.dropdown_menu li
  display: none
  color: white
  background-color: #34495e
  padding: 10px 20px
  font-size: 16px
  opacity: 0
  &:hover
    background-color: #2980b9

.dropdown:hover .dropdown_menu li
     display: block
```

Animating the whole menu meant we only needed to add an animation for each menu, but animating each menu item individually means we need to add a separate `animation` property for each menu item. Fortunately, all menu items will share the same `@keyframe` rule, same duration, same timing function, same `animation-fill-mode`, and same `transform-origin` property. The only property that needs to be different is the [animation-delay](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-delay) to stagger each item's animation.

We could manually write a CSS selector for each dropdown menu item with a different animation-delay or we can take advantage of Stylus's [for loop](https://stylus-lang.com/docs/iteration.html) to iterate over each and generate those selectors automatically. The end result will be the same so let's use a `for` loop to make our lives easier.

There are five menu items for each dropdown, to iterate five times we can use the following syntax: `for num in (1..5)`. Each `dropdown_item` has a class with a sequential number: `dropdown_item-1`, `dropdown_item-2`, etc. Therefore, we can select each by using the `num` variable and [selector interpolation](https://stylus-lang.com/docs/interpolation.html#selector-interpolation): `.dropdown_item-{num}`. Finally, we can use the `num` variable to delay each item by an even amount. If the animation duration is `300ms` and there are 5 items overall, then we can delay each item by `60ms` increments: `(num * 60ms)`. Putting this all together produces the following:

```sass
.dropdown_menu-2
  for num in (1..5)
     .dropdown_item-{num}
        animation: rotateX 300ms (num * 60ms) ease-in-out forwards
        transform-origin: top center
```

The `@keyframe` rules are almost the exact same as the whole menu animations, with the exception of also animating `opacity` from `0` to `1`.

```sass
@keyframes rotateX {
  0% {
    opacity: 0;
    transform: rotateX(-90deg);
  }
  50% {
    transform: rotateX(-20deg);
  }
  100% {
    opacity: 1;
    transform: rotateX(0deg);
  }
}
```