@@ -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 >
0 commit comments