Animating to display none
The transition works when we close the navigation menu because the browser is waiting until the transition is finished, then changing display: block
to display: none
.
It can't transition between those two, it simply allows all the other transitions to run, then flips that switch at the end.
When we go the other way around, it must immediately switch to display: block
, because if it left it as none
, we wouldn't see anything.
But when an element switches to display: block
, it will be in it's "end state". This is a limitation of how CSS works.
To make this possible, we have a new at-rule: @starting-style
.
We can use this to provide starting styles to the browser when something is rendered on the page.
So in our case, we can do this:
[aria-expanded="true"] + .primary-navigation {
display: block;
opacity: 1;
@starting-style {
display: block;
opacity: 0;
}
}
In doing this, when it first makes the switch to display: block
, it's now coming in with an opacity: 0
as well, which will then transition to the ending value we provided above.
And while I feel like it seems a bit strange, we must declare the @starting-style
after the final values, because of the way the cascade works.
In fact, we have to work a little backwards:
.selector {
/* CLOSED STATE */
/* animate to this on exit */
}
.selector.opened {
/* FINAL OPENED STATE */
@starting-style {
/* ANIMATE IN FROM THIS */
/* animate to this on entry */
}
}
Entry and exit animations
Using @starting-style
can feel a little repetitive when the entry and exit animations are the same, but it opens the door for having a different transition in each direction.
.primary-navigation {
/* exit to the right */
transform: translateX(-100%);
}
[aria-expanded="true"] + .primary-navigation {
transform: translateX(0%);
@starting-style {
/* enter from the top */
transform: translateY(-100%);
}
}