|
1 | 1 | --- |
2 | 2 | import { navigationItems } from '@components/Navigation/server' |
| 3 | +import menuStyles from './menu.module.css' |
3 | 4 | export interface Props { |
4 | 5 | path: string |
5 | 6 | } |
6 | 7 |
|
7 | 8 | const { path } = Astro.props |
8 | 9 |
|
9 | 10 | const activeMenuItem = (url: string) => { |
10 | | - return path === url ? 'nav-item-active' : '' |
| 11 | + return path === url ? ['nav-item-active', menuStyles['navItemActive']] : [] |
11 | 12 | } |
12 | 13 | --- |
13 | 14 |
|
14 | | -<style> |
15 | | - /* No-scroll class used by toggleMenu() method in Navigation script */ |
16 | | - :global(.no-scroll) { |
17 | | - overflow: hidden; |
18 | | - } |
19 | | - |
20 | | - /* Mobile menu - hidden by default */ |
21 | | - .main-nav-menu { |
22 | | - display: none; |
23 | | - } |
24 | | - |
25 | | - /* Mobile menu items - initially hidden with transform */ |
26 | | - .main-nav-item { |
27 | | - opacity: 0; |
28 | | - transform: translateY(-1rem); |
29 | | - transition: opacity 0.4s ease, transform 0.4s ease; |
30 | | - } |
31 | | - |
32 | | - /* Desktop menu - visible */ |
33 | | - @media (width >= 1024px) { |
34 | | - .main-nav-menu { |
35 | | - display: flex; |
36 | | - } |
37 | | - |
38 | | - .main-nav-item { |
39 | | - opacity: 1; |
40 | | - transform: translateY(0); |
41 | | - } |
42 | | - } |
43 | | - |
44 | | - /* Staggered animation delays for mobile menu items - start after splash completes */ |
45 | | - @media (width <= 1023px) { |
46 | | - :global(#header.aria-expanded-true) .main-nav-menu.menu-visible .main-nav-item:nth-child(1) { |
47 | | - transition-delay: 0.1s !important; |
48 | | - } |
49 | | - |
50 | | - :global(#header.aria-expanded-true) .main-nav-menu.menu-visible .main-nav-item:nth-child(2) { |
51 | | - transition-delay: 0.2s !important; |
52 | | - } |
53 | | - |
54 | | - :global(#header.aria-expanded-true) .main-nav-menu.menu-visible .main-nav-item:nth-child(3) { |
55 | | - transition-delay: 0.3s !important; |
56 | | - } |
57 | | - |
58 | | - :global(#header.aria-expanded-true) .main-nav-menu.menu-visible .main-nav-item:nth-child(4) { |
59 | | - transition-delay: 0.4s !important; |
60 | | - } |
61 | | - |
62 | | - :global(#header.aria-expanded-true) .main-nav-menu.menu-visible .main-nav-item:nth-child(5) { |
63 | | - transition-delay: 0.5s !important; |
64 | | - } |
65 | | - |
66 | | - :global(#header.aria-expanded-true) .main-nav-menu.menu-visible .main-nav-item:nth-child(6) { |
67 | | - transition-delay: 0.6s !important; |
68 | | - } |
69 | | - |
70 | | - /* Mobile menu items - remove desktop underlines */ |
71 | | - :global(#header.aria-expanded-true) .main-nav-item a { |
72 | | - text-decoration: none !important; |
73 | | - } |
74 | | - |
75 | | - /* Mobile menu item - remove focus outline */ |
76 | | - :global(#header.aria-expanded-true) .main-nav-item a:focus { |
77 | | - outline: none !important; |
78 | | - } |
79 | | - |
80 | | - /* Mobile menu item hover/focus/active underline effects */ |
81 | | - :global(#header.aria-expanded-true) .main-nav-item a:hover, |
82 | | - :global(#header.aria-expanded-true) .main-nav-item a:focus, |
83 | | - :global(#header.aria-expanded-true) .main-nav-item a:active { |
84 | | - outline: none !important; |
85 | | - text-decoration: underline !important; |
86 | | - text-decoration-color: var(--color-bg) !important; |
87 | | - text-decoration-style: dotted !important; |
88 | | - text-decoration-thickness: 2px !important; |
89 | | - text-underline-offset: 8px !important; |
90 | | - } |
91 | | - } |
92 | | - |
93 | | - /* Mobile splash background animation */ |
94 | | - |
95 | | - /* |
96 | | - Use a fixed, centered pseudo-element that scales from the |
97 | | - toggle button origin to cover the entire viewport. Keep the |
98 | | - splash slightly translucent and ensure it sits *under* the |
99 | | - menu items so links can appear above it. |
100 | | - */ |
101 | | - :global(#mobile-splash) { |
102 | | - /* anchor point for the splash (small element near toggle) */ |
103 | | - height: 1px; |
104 | | - pointer-events: none; |
105 | | - position: fixed; |
106 | | - |
107 | | - /* keep near the top-right where the hamburger lives but respect safe-area */ |
108 | | - right: env(safe-area-inset-right, 20px); |
109 | | - top: env(safe-area-inset-top, 20px); |
110 | | - width: 1px; |
111 | | - z-index: var(--z-mobile-splash); |
112 | | - } |
113 | | - |
114 | | - :global(#mobile-splash::after) { |
115 | | - /* keep the splash slightly translucent so page chrome is visible under it */ |
116 | | - background-color: var(--color-bg-inverse); |
117 | | - border-radius: 50%; |
118 | | - content: ''; |
119 | | - height: 300vmax; |
120 | | - opacity: 1; |
121 | | - pointer-events: none; |
122 | | - position: fixed; |
123 | | - |
124 | | - /* Position the circle center at the hamburger button location */ |
125 | | - right: calc(env(safe-area-inset-right, 20px) + 20px); /* Adjust to button center */ |
126 | | - top: calc(env(safe-area-inset-top, 20px) + 20px); /* Adjust to button center */ |
127 | | - |
128 | | - /* Center the circle on this point */ |
129 | | - transform: translate(50%, -50%) scale(0); |
130 | | - transform-origin: center; |
131 | | - transition: transform 0.55s cubic-bezier(0.755, 0.05, 0.855, 0.06); |
132 | | - |
133 | | - /* make it big enough to guarantee full coverage on all viewports */ |
134 | | - width: 300vmax; |
135 | | - will-change: transform, opacity; |
136 | | - |
137 | | - /* place the splash above everything except nav menu items and close button */ |
138 | | - z-index: var(--z-mobile-splash); |
139 | | - } |
140 | | - |
141 | | - /* Mobile menu open state splash - scale up to cover viewport */ |
142 | | - /* stylelint-disable-next-line selector-max-specificity */ |
143 | | - :global(#header.aria-expanded-true #mobile-splash::after) { |
144 | | - transform: translate(50%, -50%) scale(1); |
145 | | - } |
146 | | - |
147 | | - /* Keep splash backdrop non-interactive */ |
148 | | - |
149 | | - /* Mobile menu open state display */ |
150 | | - :global(#header.aria-expanded-true) .main-nav { |
151 | | - /* ensure menu items sit above the splash (120 > 110) */ |
152 | | - z-index: var(--z-mobile-menu); |
153 | | - } |
154 | | - |
155 | | - :global(#header.aria-expanded-true) .main-nav-menu { |
156 | | - align-items: center; |
157 | | - |
158 | | - /* When open, make the menu full-screen and center items. We keep this |
159 | | - in CSS because it's only applied when the header has the expanded |
160 | | - class and relies on fixed positioning and z-index coordination. |
161 | | - */ |
162 | | - display: flex; |
163 | | - flex-direction: column; |
164 | | - height: 100vh; |
165 | | - justify-content: center; |
166 | | - left: 0; |
167 | | - |
168 | | - /* Delay menu appearance until splash covers viewport */ |
169 | | - opacity: 0; |
170 | | - position: fixed; |
171 | | - top: 0; |
172 | | - transition: opacity 0.3s ease 0.5s; |
173 | | - width: 100vw; |
174 | | - |
175 | | - /* ensure menu items sit above the splash (120 > 110) */ |
176 | | - z-index: var(--z-mobile-menu); |
177 | | - } |
178 | | - |
179 | | - :global(#header.aria-expanded-true) .main-nav-menu.menu-visible { |
180 | | - opacity: 1; |
181 | | - } |
182 | | - |
183 | | - :global(#header.aria-expanded-true) .main-nav-menu.menu-visible .main-nav-item { |
184 | | - opacity: 1; |
185 | | - transform: translateY(0); |
186 | | - |
187 | | - /* ensure menu items sit above the splash (120 > 110) */ |
188 | | - z-index: var(--z-mobile-menu); |
189 | | - } |
190 | | - |
191 | | - :global(#header.aria-expanded-true) .main-nav-item a { |
192 | | - color: var(--color-bg) !important; |
193 | | - opacity: 1; |
194 | | - |
195 | | - /* ensure menu items sit above the splash (120 > 110) */ |
196 | | - z-index: var(--z-mobile-menu-item) !important; |
197 | | - } |
198 | | - |
199 | | - /* Desktop navigation item underline effects */ |
200 | | - @media (width >= 1024px) { |
201 | | - .main-nav-item a { |
202 | | - color: var(--color-primary-offset); |
203 | | - text-decoration: underline; |
204 | | - text-decoration-color: var(--color-primary-offset); |
205 | | - text-decoration-style: dotted; |
206 | | - text-decoration-thickness: 2px; |
207 | | - text-underline-offset: 8px; |
208 | | - transition: color 0.2s, text-decoration-color 0.2s; |
209 | | - } |
210 | | - |
211 | | - .nav-item-active a { |
212 | | - color: var(--color-primary); |
213 | | - text-decoration: none; |
214 | | - } |
215 | | - |
216 | | - .main-nav-item a:hover, |
217 | | - .main-nav-item a:focus { |
218 | | - color: var(--color-primary); |
219 | | - text-decoration-color: transparent; |
220 | | - } |
221 | | - |
222 | | - .main-nav-item::after { |
223 | | - background-color: var(--color-primary); |
224 | | - bottom: -0.25rem; |
225 | | - content: ''; |
226 | | - display: block; |
227 | | - height: 0.25rem; |
228 | | - left: 1.25rem; |
229 | | - position: absolute; |
230 | | - right: 1.25rem; |
231 | | - transform: scaleX(0); |
232 | | - transform-origin: 0 50%; |
233 | | - transition: transform 0.3s cubic-bezier(0.86, 0, 0.07, 1) 0.1s; |
234 | | - } |
235 | | - |
236 | | - .main-nav-item:hover::after { |
237 | | - transform: scaleX(1); |
238 | | - } |
239 | | - |
240 | | - .nav-item-active::after { |
241 | | - background-color: var(--color-primary); |
242 | | - transform: none; |
243 | | - } |
244 | | - |
245 | | - .nav-item-active:hover::after { |
246 | | - background-color: var(--color-primary); |
247 | | - } |
248 | | - } |
249 | | -</style> |
250 | | - |
251 | 15 | <nav id="main-nav" class="main-nav" role="navigation" aria-label="Main"> |
252 | 16 | <ul |
253 | | - class="main-nav-menu flex flex-row justify-center relative lg:items-center lg:flex-row" |
| 17 | + class:list={[ |
| 18 | + 'main-nav-menu', |
| 19 | + menuStyles['mainNavMenu'], |
| 20 | + 'w-full font-heading text-[0.85rem] uppercase tracking-[0.22em] text-primary', |
| 21 | + 'lg:relative lg:text-[0.8rem] lg:tracking-[0.12em]' |
| 22 | + ]} |
254 | 23 | tabindex="-1" |
255 | 24 | aria-label="main navigation" |
256 | 25 | > |
257 | 26 | { |
258 | 27 | navigationItems.map(navItem => ( |
259 | | - <li class:list={['main-nav-item lg:relative', activeMenuItem(navItem.url)]}> |
| 28 | + <li class:list={['main-nav-item', menuStyles['mainNavItem'], 'lg:relative', ...activeMenuItem(navItem.url)]}> |
260 | 29 | <a |
261 | 30 | href={navItem.url} |
262 | | - class="block text-primary text-xl lg:text-lg font-bold tracking-[5px] lg:tracking-normal p-4 lg:px-5 lg:py-3 text-center uppercase lg:lowercase no-underline lg:hover:text-bg-offset lg:hover:bg-inherit lg:focus:text-bg-offset lg:focus:bg-inherit focus:outline-0 lg:flex lg:items-center lg:relative lg:whitespace-nowrap lg:z-10" |
| 31 | + class:list={[ |
| 32 | + 'block text-primary text-xl font-bold tracking-[5px] uppercase no-underline text-center p-4 focus:outline-0', |
| 33 | + 'lg:text-lg lg:lowercase lg:tracking-normal lg:px-5 lg:py-3 lg:flex lg:items-center lg:relative lg:whitespace-nowrap lg:z-10', |
| 34 | + menuStyles['mainNavLink'] |
| 35 | + ]} |
263 | 36 | > |
264 | 37 | {navItem.title} |
265 | 38 | </a> |
|
0 commit comments