Skip to content

Commit 43115bf

Browse files
committed
Animate nav opening
1 parent c077b4d commit 43115bf

3 files changed

Lines changed: 61 additions & 9 deletions

File tree

src/components/page/header.astro

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import {
3737
<template id={menuTemplateId}>
3838
<button
3939
id={menuButtonId}
40-
class="menu no-button"
40+
class="menu no-button transition-transform"
4141
aria-labelledby={menuTitleId}
4242
>
4343
<svg
@@ -121,14 +121,42 @@ import {
121121
const menuButton = document.getElementById(menuButtonId) as HTMLButtonElement;
122122
const modeButton = document.getElementById(modeButtonId) as HTMLButtonElement;
123123

124-
menuButton.classList.add("transition-transform");
125124
menuButton.addEventListener("click", () => {
126125
document.body.classList.toggle("open-nav");
127126
header.classList.toggle("open-nav");
128-
navList.classList.toggle("open-nav");
129-
modeButton.classList.toggle("open-nav");
130-
131127
menuButton.classList.toggle("rotate-left");
128+
129+
navList.classList.toggle("appear");
130+
modeButton.classList.toggle("appear");
131+
132+
if (navList.classList.contains("appear")) {
133+
navList.classList.add("open-nav");
134+
modeButton.classList.add("open-nav");
135+
} else {
136+
// This prevents a scrollbar from appearing while the header expands and
137+
// contracts.
138+
header.style.overflowY = "hidden";
139+
140+
// 'setTimeout' is needed because removing 'appear' and adding 'vanish'
141+
// at the same time negates the 'vanish' animation.
142+
setTimeout(() => {
143+
navList.classList.add("vanish");
144+
modeButton.classList.add("vanish");
145+
});
146+
}
147+
});
148+
149+
// 'navList' and 'modeButton' animations end at the same time so put
150+
// 'animationend' handling on only one of them.
151+
navList.addEventListener("animationend", ({ target }) => {
152+
if ((target as HTMLUListElement).classList.contains("appear")) {
153+
header.style.overflowY = "auto";
154+
} else {
155+
navList.classList.remove("open-nav");
156+
modeButton.classList.remove("open-nav");
157+
navList.classList.remove("vanish");
158+
modeButton.classList.remove("vanish");
159+
}
132160
});
133161

134162
const darkModeMedia = matchMedia("(prefers-color-scheme: dark)");
@@ -203,8 +231,8 @@ import {
203231
darkModeMedia.removeEventListener("change", setModeTitleBasedOnMedia);
204232
}
205233

206-
// Mode moon and sun animations end at the same time so put 'animationend'
207-
// handling on only one of them.
234+
// 'modeMoon' and 'modeSun' animations end at the same time so put
235+
// 'animationend' handling on only one of them.
208236
modeMoon.addEventListener("animationend", () => {
209237
const storedMode = modeStore.get();
210238

@@ -240,7 +268,7 @@ import {
240268
return;
241269
}
242270

243-
// Mode SVG should not animate at initial page load.
271+
// 'modeMoon' and 'modeSun' should not animate at initial page load.
244272
setMode(storedMode, false);
245273
});
246274
</script>

src/styles/header.scss

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@
4747
display: grid;
4848
gap: v-size(5);
4949
justify-items: center;
50+
height: v-size(14);
5051
margin-bottom: v-size(10);
51-
overflow: hidden auto;
52+
overflow: hidden;
5253
}
5354
.logo {
5455
justify-self: start;
@@ -87,10 +88,17 @@
8788
display: block;
8889
}
8990

91+
@include m-motion {
92+
.header {
93+
transition: height 250ms ease-in-out;
94+
}
95+
}
96+
9097
// NOTE: Change to 'm-large' when more nav items are added
9198
@include m-medium {
9299
.header {
93100
grid-template-columns: 1fr 2fr 1fr;
101+
height: 100%;
94102
}
95103
.menu {
96104
display: none;

src/styles/page.scss

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,12 @@ p.larger-spacing {
148148
.rotate-left {
149149
transform: rotateZ(-90deg);
150150
}
151+
.appear {
152+
animation: 0ms ease-in-out forwards appear;
153+
}
154+
.vanish {
155+
animation: 0ms ease-in-out reverse forwards appear;
156+
}
151157
.rotate-appear {
152158
animation: 0ms ease-in-out forwards rotate-appear;
153159
}
@@ -159,12 +165,22 @@ p.larger-spacing {
159165
.transition-transform {
160166
transition: transform 250ms ease-in-out;
161167
}
168+
.appear,
169+
.vanish,
162170
.rotate-appear,
163171
.rotate-vanish {
164172
animation-duration: 250ms;
165173
}
166174
}
167175

176+
@keyframes appear {
177+
from {
178+
opacity: 0;
179+
}
180+
to {
181+
opacity: 1;
182+
}
183+
}
168184
@keyframes rotate-appear {
169185
from {
170186
opacity: 0;

0 commit comments

Comments
 (0)