On a phone, a university homepage puts almost all of its navigation behind one button in
the corner. For a lot of visitors that button is the first thing they press. Most sites
answer that press with three lines snapping into an X, or with nothing at all until the
menu slides in.
That three-lines-to-X turn is everywhere, so I left it out. These ten are alternatives you
don’t see often, ordered from the quietest to the most crafted. Each plays once as it
scrolls into view and again when you hover its card. Press any button to run it yourself,
switch to quarter speed to see what each line is doing, and open “View code” for the CSS
or Sass.
Real time Quarter speed
Open all
1 Spring settleCSS linear() The X turns in on a spring, passes 45° by a few degrees and settles.
700 ms
View code
1 Spring settle× CSS Sass
/* A damped spring sampled into linear(): it passes 45° by about 10%, about 4.5°,
then settles. Only the turn springs; the lines reach the centre on a plain curve. */
0 , 0.046 , 0.161 , 0.312 , 0.474 , 0.631 , 0.77 , 0.884 , 0.972 , 1.035 , 1.074 , 1.095 ,
1.101 , 1.096 , 1.084 , 1.069 , 1.053 , 1.037 , 1.023 , 1.011 , 1.003 , 0.996 , 0.992 , 0.99 , 1
. menu-button--spring . line {
translate 340 ms var ( --ease-out ),
rotate 340 ms var ( --ease-out ),
. menu-button--spring [ aria-expanded = true ] . line {
translate 300 ms var ( --ease-out ),
rotate 640 ms var ( --spring ) 60 ms ,
// A damped spring sampled into linear(): it passes 45° by about 10%, about 4.5°,
// then settles. Only the turn springs; the lines reach the centre on a plain curve.
0 , 0.046 , 0.161 , 0.312 , 0.474 , 0.631 , 0.77 , 0.884 , 0.972 , 1.035 , 1.074 , 1.095 ,
1.101 , 1.096 , 1.084 , 1.069 , 1.053 , 1.037 , 1.023 , 1.011 , 1.003 , 0.996 , 0.992 , 0.99 , 1
translate 340 ms var ( --ease-out ) ,
rotate 340 ms var ( --ease-out ) ,
& [ aria-expanded = ' true ' ] . line {
translate 300 ms var ( --ease-out ) ,
rotate 640 ms var ( --spring ) 60 ms ,
2 Point bloomCSS The lines gather into a dot, and the X grows out of it.
680 ms
View code
2 Point bloom× CSS Sass
/* The lines gather into a 2px dot at the centre, and the X grows out of that dot.
The dot holds for a beat, and both halves scale to exactly its size, so the swap is seamless. */
. menu-button--bloom . line {
translate 280 ms var ( --ease-in-out ) 340 ms ,
scale 280 ms var ( --ease-in-out ) 340 ms ,
. menu-button--bloom . x-line {
scale 340 ms var ( --ease-in-out ),
visibility 0 s linear 340 ms ;
. menu-button--bloom [ aria-expanded = true ] . line {
translate 280 ms var ( --ease-in-out ),
scale 280 ms var ( --ease-in-out ),
. menu-button--bloom [ aria-expanded = true ] . line-1 {
. menu-button--bloom [ aria-expanded = true ] . line-3 {
. menu-button--bloom [ aria-expanded = true ] . x-line {
scale 340 ms var ( --ease-out ) 340 ms ,
visibility 0 s linear 340 ms ;
// The lines gather into a 2px dot at the centre, and the X grows out of that dot.
// The dot holds for a beat, and both halves scale to exactly its size, so the swap is seamless.
translate $gather var ( --ease-in-out ) $grow,
scale $gather var ( --ease-in-out ) $grow,
scale $grow var ( --ease-in-out ) ,
visibility 0 s linear $grow ;
& [ aria-expanded = ' true ' ] {
translate $gather var ( --ease-in-out ) ,
scale $gather var ( --ease-in-out ) ,
opacity 0 s linear $gather + 60 ms ;
. line-1 { translate : 0 7 px ; }
. line-3 { translate : 0 -7 px ; }
scale $grow var ( --ease-out ) $gather + 60 ms ,
visibility 0 s linear $gather + 60 ms ;
3 FoldCSS The outer lines fold at the middle and meet as an X.
420 ms
View code
3 Fold× CSS Sass
/* The outer lines are each split into two halves hinged at the middle. Each half turns
on its inner end, so the top line folds into a V, the bottom into an inverted V, and
the two points meet in the centre as an X. */
. menu-button--fold . half {
translate 420 ms var ( --ease-in-out ),
rotate 420 ms var ( --ease-in-out );
. menu-button--fold . half-left {
border-radius : 2 px 0 0 2 px ;
transform-origin : right center ;
. menu-button--fold . half-right {
border-radius : 0 2 px 2 px 0 ;
transform-origin : left center ;
/* The middle line draws into the hinge as the halves arrive. */
. menu-button--fold . line-2 {
transition : scale 300 ms var ( --ease-in-out ) 120 ms ;
. menu-button--fold [ aria-expanded = true ] . line-1 {
. menu-button--fold [ aria-expanded = true ] . line-3 {
. menu-button--fold [ aria-expanded = true ] . line-1 . half-left ,
. menu-button--fold [ aria-expanded = true ] . line-3 . half-right {
. menu-button--fold [ aria-expanded = true ] . line-1 . half-right ,
. menu-button--fold [ aria-expanded = true ] . line-3 . half-left {
. menu-button--fold [ aria-expanded = true ] . line-2 {
transition : scale 300 ms var ( --ease-in-out );
// The outer lines are each split into two halves hinged at the middle. Each half turns
// on its inner end, so the top line folds into a V, the bottom into an inverted V, and
// the two points meet in the centre as an X.
translate $duration var ( --ease-in-out ) ,
rotate $duration var ( --ease-in-out );
border-radius : 2 px 0 0 2 px ;
transform-origin : right center ;
border-radius : 0 2 px 2 px 0 ;
transform-origin : left center ;
// The middle line draws into the hinge as the halves arrive.
transition : scale 300 ms var ( --ease-in-out ) 120 ms ;
& [ aria-expanded = ' true ' ] {
. line-1 { translate : 0 7 px ; }
. line-3 { translate : 0 -7 px ; }
. line-3 . half-right { rotate : 45 deg ; }
. line-3 . half-left { rotate : -45 deg ; }
transition : scale 300 ms var ( --ease-in-out );
4 ConvergeCSS The lines thin away as the arms of the X slide in from the corners.
640 ms
View code
4 Converge× CSS Sass
/* The X is four half-arms that rest 5px out along their own diagonals, hidden.
Opening thins the lines away and draws the arms in to meet at the centre. */
. menu-button--converge . line {
scale 300 ms var ( --ease-in-out ) 160 ms ,
opacity 300 ms ease 160 ms ;
. menu-button--converge . arm {
background : currentColor ;
translate 300 ms var ( --ease-in-out ) var ( --delay , 0 s ),
opacity 300 ms ease var ( --delay , 0 s );
. menu-button--converge . arm-1 ,
. menu-button--converge . arm-4 {
border-radius : 2 px 0 0 2 px ;
transform-origin : right center ;
. menu-button--converge . arm-2 ,
. menu-button--converge . arm-3 {
border-radius : 0 2 px 2 px 0 ;
transform-origin : left center ;
. menu-button--converge . arm-1 {
. menu-button--converge . arm-2 {
. menu-button--converge . arm-3 {
. menu-button--converge . arm-4 {
. menu-button--converge [ aria-expanded = true ] . line {
scale 280 ms var ( --ease-in-out ),
. menu-button--converge [ aria-expanded = true ] . arm {
translate 400 ms var ( --ease-out ) calc ( var ( --delay , 0 s ) + 120 ms ),
opacity 240 ms ease calc ( var ( --delay , 0 s ) + 120 ms );
// The X is four half-arms that rest 5px out along their own diagonals, hidden.
// Opening thins the lines away and draws the arms in to meet at the centre.
scale 300 ms var ( --ease-in-out ) 160 ms ,
opacity 300 ms ease 160 ms ;
background : currentColor ;
translate 300 ms var ( --ease-in-out ) var ( --delay , 0 s ) ,
opacity 300 ms ease var ( --delay , 0 s );
border-radius : 2 px 0 0 2 px ;
transform-origin : right center ;
border-radius : 0 2 px 2 px 0 ;
transform-origin : left center ;
. arm-1 { rotate : 45 deg ; translate : -5 px -5 px ; }
. arm-2 { rotate : -45 deg ; translate : 5 px -5 px ; --delay : 40 ms ; }
. arm-3 { rotate : 45 deg ; translate : 5 px 5 px ; --delay : 80 ms ; }
. arm-4 { rotate : -45 deg ; translate : -5 px 5 px ; --delay : 120 ms ; }
& [ aria-expanded = ' true ' ] {
scale 280 ms var ( --ease-in-out ) ,
translate 400 ms var ( --ease-out ) calc ( var ( --delay , 0 s ) + 120 ms ) ,
opacity 240 ms ease calc ( var ( --delay , 0 s ) + 120 ms );
5 HandoffCSS The lines slide out to the right as the X slides in from the left.
600 ms
View code
5 Handoff× CSS Sass
/* The icon clips at its sides only, so the lines leave on the right as the X
arrives from the left, while the X's corners still clear the top and bottom. */
. menu-button--handoff . menu-icon {
clip-path : inset ( -4 px 0 );
. menu-button--handoff . line {
transition : translate 320 ms var ( --ease-in-out ) var ( --delay , 0 s );
. menu-button--handoff . line-1 {
. menu-button--handoff . line-2 {
. menu-button--handoff . line-3 {
. menu-button--handoff . x-line {
transition : translate 320 ms var ( --ease-in-out ) var ( --delay , 0 s );
. menu-button--handoff . x-line-2 {
. menu-button--handoff [ aria-expanded = true ] . line {
. menu-button--handoff [ aria-expanded = true ] . line-1 {
. menu-button--handoff [ aria-expanded = true ] . line-2 {
. menu-button--handoff [ aria-expanded = true ] . line-3 {
. menu-button--handoff [ aria-expanded = true ] . x-line {
transition : translate 380 ms var ( --ease-out ) var ( --delay , 0 s );
. menu-button--handoff [ aria-expanded = true ] . x-line-1 {
. menu-button--handoff [ aria-expanded = true ] . x-line-2 {
// The icon clips at its sides only, so the lines leave on the right as the X
// arrives from the left, while the X's corners still clear the top and bottom.
clip-path : inset ( -4 px 0 );
transition : translate 320 ms var ( --ease-in-out ) var ( --delay , 0 s );
. line-1 { --delay : 200 ms ; }
. line-2 { --delay : 150 ms ; }
. line-3 { --delay : 100 ms ; }
transition : translate 320 ms var ( --ease-in-out ) var ( --delay , 0 s );
. x-line-2 { --delay : 60 ms ; }
& [ aria-expanded = ' true ' ] {
. line-2 { --delay : 50 ms ; }
. line-3 { --delay : 100 ms ; }
transition : translate 380 ms var ( --ease-out ) var ( --delay , 0 s );
. x-line-1 { --delay : 160 ms ; }
. x-line-2 { --delay : 220 ms ; }
6 LabelledCSS “Menu” turns to “Close” one letter at a time as the icon turns.
440 ms
View code
6 Labelled× CSS Sass
. menu-button--labelled . line {
translate 340 ms var ( --ease-out ),
rotate 340 ms var ( --ease-out ),
/* "Menu" and "Close" share one cell in a one-line window. Each letter carries its
index as --i, so one rule staggers the swap from left to right. */
. menu-button--labelled . menu-words {
. menu-button--labelled . menu-words > span {
. menu-button--labelled . menu-words i {
transition : translate 320 ms var ( --ease-out ) calc ( var ( --i ) * 30 ms );
. menu-button--labelled . menu-words-close i {
. menu-button--labelled [ aria-expanded = true ] . menu-words-menu i {
. menu-button--labelled [ aria-expanded = true ] . menu-words-close i {
translate 340 ms var ( --ease-out ) ,
rotate 340 ms var ( --ease-out ) ,
// "Menu" and "Close" share one cell in a one-line window. Each letter carries its
// index as --i, so one rule staggers the swap from left to right.
transition : translate 320 ms var ( --ease-out ) calc ( var ( --i ) * 30 ms );
& [ aria-expanded = ' true ' ] {
7 RingCSS and SVG A thin gold ring draws around the finished X.
720 ms
View code
7 Ring× CSS Sass
. menu-button--ring . line {
translate 340 ms var ( --ease-out ),
rotate 340 ms var ( --ease-out ),
/* Starts the draw at twelve o'clock. */
. menu-button--ring . ring {
/* The dash runs 2% past the circle so the join never shows a seam,
and visibility hides the resting dash, which still paints a dot. */
. menu-button--ring . ring circle {
stroke-dashoffset 240 ms ease-in ,
. menu-button--ring [ aria-expanded = true ] . ring circle {
stroke-dashoffset 520 ms var ( --ease-out ) 200 ms ,
visibility 520 ms linear 200 ms ;
translate 340 ms var ( --ease-out ) ,
rotate 340 ms var ( --ease-out ) ,
// Starts the draw at twelve o'clock.
// The dash runs 2% past the circle so the join never shows a seam,
// and visibility hides the resting dash, which still paints a dot.
stroke-dashoffset 240 ms ease-in,
& [ aria-expanded = ' true ' ] . ring circle {
stroke-dashoffset 520 ms var ( --ease-out ) 200 ms ,
visibility 520 ms linear 200 ms ;
8 Leaf3D CSS Lines turn away like pages and the X turns in.
580 ms
View code
8 Leaf× CSS Sass
/* Each line reads its delay from --delay, so the stagger flips
between opening and closing by changing the numbers alone. */
. menu-button--leaf . menu-icon {
. menu-button--leaf . line {
backface-visibility : hidden ;
rotate 280 ms var ( --ease-in-out ) var ( --delay , 0 s ),
opacity 0 s linear var ( --delay , 0 s );
. menu-button--leaf . line-1 {
. menu-button--leaf . line-2 {
. menu-button--leaf . line-3 {
/* The X lines rest edge-on, which still paints a hairline, so they hide too. */
. menu-button--leaf . leaf-x {
background : currentColor ;
transform : rotateX ( 90 deg );
transform 300 ms var ( --ease-out ) var ( --delay , 0 s ),
visibility 300 ms linear var ( --delay , 0 s );
. menu-button--leaf . leaf-x-1 {
. menu-button--leaf . leaf-x-2 {
. menu-button--leaf [ aria-expanded = true ] . line {
rotate 280 ms var ( --ease-in-out ) var ( --delay , 0 s ),
opacity 80 ms linear calc ( var ( --delay , 0 s ) + 200 ms );
. menu-button--leaf [ aria-expanded = true ] . line-1 {
. menu-button--leaf [ aria-expanded = true ] . line-2 {
. menu-button--leaf [ aria-expanded = true ] . line-3 {
. menu-button--leaf [ aria-expanded = true ] . leaf-x {
transform : rotateX ( 0 deg );
. menu-button--leaf [ aria-expanded = true ] . leaf-x-1 {
. menu-button--leaf [ aria-expanded = true ] . leaf-x-2 {
// Each line reads its delay from --delay, so the stagger flips
// between opening and closing by changing the numbers alone.
backface-visibility : hidden ;
rotate 280 ms var ( --ease-in-out ) var ( --delay , 0 s ) ,
opacity 0 s linear var ( --delay , 0 s );
. line-1 { --delay : 200 ms ; }
. line-2 { --delay : 250 ms ; }
. line-3 { --delay : 300 ms ; }
// The X lines rest edge-on, which still paints a hairline, so they hide too.
background : currentColor ;
transform : rotateX ( 90 deg );
transform 300 ms var ( --ease-out ) var ( --delay , 0 s ) ,
visibility 300 ms linear var ( --delay , 0 s );
. leaf-x-1 { rotate : 45 deg ; --delay : 0 s ; }
. leaf-x-2 { rotate : -45 deg ; --delay : 60 ms ; }
& [ aria-expanded = ' true ' ] {
rotate 280 ms var ( --ease-in-out ) var ( --delay , 0 s ) ,
opacity 80 ms linear calc ( var ( --delay , 0 s ) + 200 ms );
. line-2 { --delay : 50 ms ; }
. line-3 { --delay : 100 ms ; }
transform : rotateX ( 0 deg );
. leaf-x-1 { --delay : 220 ms ; }
. leaf-x-2 { --delay : 280 ms ; }
9 Pen strokesSVG Lines erase left to right, then the X is drawn.
720 ms
View code
9 Pen strokes× CSS Sass
/* pathLength="1" on every path keeps the dash maths independent of length. */
. menu-button--pen . pen path {
stroke-dashoffset var ( --duration , 240 ms ) var ( --ease-in-out ) var ( --delay , 0 ms ),
visibility var ( --duration , 240 ms ) linear var ( --delay , 0 ms );
/* Round caps paint a dot even on an empty dash, so hidden strokes use visibility. */
. menu-button--pen . pen-cross {
. menu-button--pen . pen-line-1 {
. menu-button--pen . pen-line-2 {
. menu-button--pen . pen-line-3 {
. menu-button--pen . pen-cross-2 {
/* Offsetting to -1 erases each line in the direction it was drawn. */
. menu-button--pen [ aria-expanded = true ] . pen-line {
. menu-button--pen [ aria-expanded = true ] . pen-line-1 {
. menu-button--pen [ aria-expanded = true ] . pen-line-2 {
. menu-button--pen [ aria-expanded = true ] . pen-line-3 {
. menu-button--pen [ aria-expanded = true ] . pen-cross {
. menu-button--pen [ aria-expanded = true ] . pen-cross-1 {
. menu-button--pen [ aria-expanded = true ] . pen-cross-2 {
// pathLength="1" on every path keeps the dash maths independent of length.
stroke-dashoffset var ( --duration , 240 ms ) var ( --ease-in-out ) var ( --delay , 0 ms ) ,
visibility var ( --duration , 240 ms ) linear var ( --delay , 0 ms );
// Round caps paint a dot even on an empty dash, so hidden strokes use visibility.
. pen-line-1 { --delay : 200 ms ; }
. pen-line-2 { --delay : 250 ms ; }
. pen-line-3 { --delay : 300 ms ; }
. pen-cross-2 { --delay : 60 ms ; }
& [ aria-expanded = ' true ' ] {
// Offsetting to -1 erases each line in the direction it was drawn.
. pen-line-1 { --delay : 0 ms ; }
. pen-line-2 { --delay : 60 ms ; }
. pen-line-3 { --delay : 120 ms ; }
. pen-cross-1 { --delay : 260 ms ; }
. pen-cross-2 { --delay : 420 ms ; }
10 SealSVG Pen strokes plus 24 ticks setting like a seal’s rim.
1160 ms
View code
10 Seal× CSS Sass
/* Builds on Pen strokes: the button carries both classes. */
. menu-button--seal . seal {
transition : rotate 320 ms ease-in ;
/* Each of the 24 ticks has its index in --i, so one rule staggers them all. */
. menu-button--seal . seal line {
transform-origin : 24 px 24 px ;
opacity 160 ms ease calc ( ( 23 - var ( --i )) * 5 ms ),
scale 200ms ease calc((23 - var(--i)) * 5ms) ;
. menu-button--seal . seal circle {
stroke-dashoffset 240 ms ease-in ,
. menu-button--seal [ aria-expanded = true ] . seal {
transition : rotate 1000 ms var ( --ease-out ) 160 ms ;
. menu-button--seal [ aria-expanded = true ] . seal line {
opacity 220 ms ease calc ( 200 ms + var ( --i ) * 20 ms ),
scale 380 ms var ( --ease-out ) calc ( 200 ms + var ( --i ) * 20 ms );
. menu-button--seal [ aria-expanded = true ] . seal circle {
stroke-dashoffset 640 ms var ( --ease-out ) 380 ms ,
visibility 640 ms linear 380 ms ;
// Builds on Pen strokes: the button carries both classes.
transition : rotate 320 ms ease-in ;
// Each of the 24 ticks has its index in --i, so one rule staggers them all.
transform-origin : 24 px 24 px ;
opacity 160 ms ease calc (( 23 - var ( --i )) * 5 ms ) ,
scale 200 ms ease calc (( 23 - var ( --i )) * 5 ms );
stroke-dashoffset 240 ms ease-in,
& [ aria-expanded = ' true ' ] {
transition : rotate 1000 ms var ( --ease-out ) 160 ms ;
opacity 220 ms ease calc ( 200 ms + var ( --i ) * 20 ms ) ,
scale 380 ms var ( --ease-out ) calc ( 200 ms + var ( --i ) * 20 ms );
stroke-dashoffset 640 ms var ( --ease-out ) 380 ms ,
visibility 640 ms linear 380 ms ;
Animation theory
My first timings came from app conventions: 140 to 220 ms. On a university header they
read as a flicker. Every main motion here now runs at least 300 ms, staged beats at least
200 ms, and closing runs a little faster than opening, since by then the visitor knows
where they are going. I also softened the ease-out curve from cubic-bezier(0.16, 1, 0.3, 1)
to cubic-bezier(0.25, 1, 0.5, 1), because the sharper one does most of its travel in the
first few frames and turns even a long animation into a jump.
Feedback the eye can follow. Jakob Nielsen puts 0.1 seconds as about the limit for a
change to feel instantaneous. Motion that finishes inside that window registers as a
swap. Slower, a visitor sees the menu icon become the close icon, which is
visibility of system status
done with motion instead of a message.
The way out stays where the way in was. All ten change the same button in place, so
the control someone just pressed is the one that closes the menu.
Slow in, slow out. Disney’s oldest animation principle. Point bloom adds
anticipation by gathering the lines before the X appears, and Spring settle adds
follow-through.
Polish is read as usability. People rate better-looking interfaces as easier to use,
the aesthetic-usability effect .
The menu button is often the first thing a visitor touches.
A wide audience. Prospective students, parents, staff, alumni and people on screen
magnifiers. Motion they can all follow beats motion that feels fast to someone who
builds websites.
The CSS keys every animation off aria-expanded, so there is no is-open class to drift
out of sync with what a screen reader announces. Swap the modifier class to change the
animation.
class = " menu-button menu-button--spring "
aria-controls = " site-menu "
< span class = " menu-icon menu-icon--cross " aria-hidden = " true " >
< i class = " line line-1 " ></ i >
< i class = " line line-2 " ></ i >
< i class = " line line-3 " ></ i >
The lines use the individual translate, rotate and scale properties instead of one
transform, so each can have its own duration and delay.
CSS Sass
/* The button every animation shares. Each one only adds its own transitions. */
--ease-out : cubic-bezier ( 0.25 , 1 , 0.5 , 1 );
--ease-in-out : cubic-bezier ( 0.65 , 0 , 0.35 , 1 );
transition : color 160 ms ease ;
. menu-button : focus-visible {
outline : 2 px solid var ( --accent );
background : currentColor ;
/* A finished X stroke, for animations that bring the X in as its own two lines. */
background : currentColor ;
/* The shared X: the outer lines meet in the middle and cross, the middle one fades. */
. menu-button [ aria-expanded = true ] . menu-icon--cross . line-1 {
. menu-button [ aria-expanded = true ] . menu-icon--cross . line-2 {
. menu-button [ aria-expanded = true ] . menu-icon--cross . line-3 {
/* Every animation becomes an instant swap. */
@ media ( prefers-reduced-motion : reduce ) {
transition-duration : 0 s !important ;
transition-delay : 0 s !important ;
// The button every animation shares. Each one only adds its own transitions.
--ease-out : cubic-bezier ( 0.25 , 1 , 0.5 , 1 );
--ease-in-out : cubic-bezier ( 0.65 , 0 , 0.35 , 1 );
transition : color 160 ms ease ;
outline : 2 px solid var ( --accent );
background : currentColor ;
// A finished X stroke, for animations that bring the X in as its own two lines.
background : currentColor ;
. x-line-1 { rotate : 45 deg ; }
. x-line-2 { rotate : -45 deg ; }
// The shared X: the outer lines meet in the middle and cross, the middle one fades.
. menu-button [ aria-expanded = ' true ' ] . menu-icon--cross {
// Every animation becomes an instant swap.
@ media ( prefers-reduced-motion : reduce ) {
transition-duration : 0 s !important ;
transition-delay : 0 s !important ;
Accessibility
The motion is the fun part, but these are the parts that matter to someone who can’t see
it or doesn’t want it:
A real <button> , so it works with a keyboard and a switch device without extra
script, with a visible focus ring.
One stable name and aria-expanded. A screen reader hears “Menu, collapsed” and then
“Menu, expanded”, not a name that changes under them.
Hidden decoration. The lines, the SVGs and the “Menu” and “Close” words are all
aria-hidden, so nothing is read twice.
Reduced motion. WCAG 2.3.3
asks that motion triggered by an interaction can be turned off. The shared base ends
with one block that zeroes durations and delays, so each button still becomes an X, in
one frame. Zeroing the delays matters as much as the durations, or the staggered ones
still wait up to 420 ms before anything changes.
Picking one
Match the motion to how often the button gets pressed. On a main university site, where
the menu is how most mobile visitors get around, stay with the quieter ones near the top
of the list. A second-long seal is a lot to sit through on every press. Save the crafted
ones for a campaign page or a microsite, where the menu gets opened once or twice.
Spring settle is the one I’d ship on a site that wants motion to stay out of the way.
Labelled is the one I’d push hardest for, because a first-time visitor gets a word instead
of a symbol. Seal belongs only on a site whose brand can carry it.
Whichever you pick, give it the same easing as the rest of the header, so the button,
the menu panel and the search drawer move like they belong together.