22const base = import .meta .env .BASE_URL .replace (/ \/ $ / , ' ' );
33const currentPath = Astro .url .pathname ;
44---
5- <header >
5+
6+ <header id =" site-header" >
67 <a href ={ base } class =" site-logo" >
78 <span class =" logo-fish" >🐟</span >
89 GuppyFisher
910 </a >
10- < nav >
11- < a href = { base } class = { currentPath === base || currentPath === base . slice ( 0 , - 1 ) ? ' active ' : ' ' } >Posts</ a >
12- <a href ={ ` ${ base }/series ` } class ={ currentPath . startsWith ( ` ${base }/series ` ) ? ' active' : ' ' } >Deep Dives </a >
11+
12+ < nav id = " main-nav " aria-label = " Main navigation " >
13+ <a href ={ base } class ={ currentPath === base || currentPath === ` ${base }/ ` ? ' active' : ' ' } >Posts </a >
1314 <a href ={ ` ${base }/series ` } class ={ currentPath .startsWith (` ${base }/series ` ) ? ' active' : ' ' } >Series</a >
1415 <a href ={ ` ${base }/about ` } class ={ currentPath === ` ${base }/about ` ? ' active' : ' ' } >About</a >
1516 </nav >
17+
1618 <div class =" header-right" >
1719 <span class =" tag-badge" >Learning in Public</span >
20+ <button
21+ id =" menu-toggle"
22+ class =" menu-toggle"
23+ aria-label =" Open menu"
24+ aria-expanded =" false"
25+ aria-controls =" main-nav"
26+ >
27+ <svg class =" icon-menu" width =" 22" height =" 22" viewBox =" 0 0 22 22" fill =" none" aria-hidden =" true" >
28+ <line x1 =" 3" y1 =" 6" x2 =" 19" y2 =" 6" stroke =" currentColor" stroke-width =" 2" stroke-linecap =" round" />
29+ <line x1 =" 3" y1 =" 11" x2 =" 19" y2 =" 11" stroke =" currentColor" stroke-width =" 2" stroke-linecap =" round" />
30+ <line x1 =" 3" y1 =" 16" x2 =" 19" y2 =" 16" stroke =" currentColor" stroke-width =" 2" stroke-linecap =" round" />
31+ </svg >
32+ <svg class =" icon-close" width =" 22" height =" 22" viewBox =" 0 0 22 22" fill =" none" aria-hidden =" true" >
33+ <line x1 =" 4" y1 =" 4" x2 =" 18" y2 =" 18" stroke =" currentColor" stroke-width =" 2" stroke-linecap =" round" />
34+ <line x1 =" 18" y1 =" 4" x2 =" 4" y2 =" 18" stroke =" currentColor" stroke-width =" 2" stroke-linecap =" round" />
35+ </svg >
36+ </button >
1837 </div >
1938</header >
2039
2140<style >
22- header {
41+ /* ── BASE ── */
42+ #site-header {
43+ position: sticky;
44+ top: 0;
45+ z-index: 200;
46+ background: var(--paper);
2347 border-bottom: 2px solid var(--ink);
2448 padding: 0 2rem;
2549 display: grid;
2650 grid-template-columns: 1fr auto 1fr;
2751 align-items: center;
28- min-height: 80px;
52+ min-height: 72px;
53+ transition: box-shadow 0.25s ease;
2954 }
3055
56+ #site-header.scrolled {
57+ box-shadow: 0 4px 24px rgba(13, 15, 20, 0.07);
58+ }
59+
60+ /* ── LOGO ── */
3161 .site-logo {
3262 font-family: 'Syne', sans-serif;
3363 font-weight: 800;
34- font-size: 1.5rem ;
64+ font-size: 1.4rem ;
3565 letter-spacing: -0.03em;
3666 text-decoration: none;
3767 color: var(--ink);
3868 display: flex;
3969 align-items: center;
40- gap: 0.5rem ;
70+ gap: 0.45rem ;
4171 }
4272
4373 .logo-fish {
4474 display: inline-block;
45- font-size: 1.8rem ;
75+ font-size: 1.6rem ;
4676 animation: swim 3s ease-in-out infinite;
4777 }
4878
@@ -51,11 +81,12 @@ const currentPath = Astro.url.pathname;
5181 50% { transform: translateY(-4px) rotate(5deg); }
5282 }
5383
84+ /* ── DESKTOP NAV ── */
5485 nav {
5586 display: flex;
5687 gap: 2rem;
5788 font-family: 'Syne', sans-serif;
58- font-size: 0.85rem ;
89+ font-size: 0.82rem ;
5990 font-weight: 700;
6091 letter-spacing: 0.08em;
6192 text-transform: uppercase;
@@ -65,13 +96,15 @@ const currentPath = Astro.url.pathname;
6596 color: var(--ink);
6697 text-decoration: none;
6798 position: relative;
68- padding-bottom: 2px ;
99+ padding-bottom: 3px ;
69100 }
70101
71102 nav a::after {
72103 content: '';
73104 position: absolute;
74- bottom: 0; left: 0; right: 100%;
105+ bottom: 0;
106+ left: 0;
107+ right: 100%;
75108 height: 2px;
76109 background: var(--accent);
77110 transition: right 0.25s ease;
@@ -80,26 +113,136 @@ const currentPath = Astro.url.pathname;
80113 nav a:hover::after,
81114 nav a.active::after { right: 0; }
82115
116+ nav a.active { color: var(--ink); }
117+
118+ /* ── RIGHT SLOT ── */
83119 .header-right {
84120 display: flex;
85121 justify-content: flex-end;
122+ align-items: center;
123+ gap: 1rem;
86124 }
87125
88126 .tag-badge {
89127 background: var(--ink);
90128 color: var(--paper);
91129 font-family: 'Syne', sans-serif;
92- font-size: 0.7rem ;
130+ font-size: 0.68rem ;
93131 font-weight: 700;
94132 letter-spacing: 0.1em;
95133 text-transform: uppercase;
96134 padding: 0.3rem 0.7rem;
97135 border-radius: 2px;
98136 }
99137
100- @media (max-width: 700px) {
101- header { grid-template-columns: 1fr auto; }
102- .header-right { display: none; }
103- nav { gap: 1rem; }
138+ /* ── HAMBURGER ── */
139+ .menu-toggle {
140+ display: none;
141+ align-items: center;
142+ justify-content: center;
143+ width: 40px;
144+ height: 40px;
145+ background: none;
146+ border: 1.5px solid var(--border);
147+ border-radius: 4px;
148+ cursor: pointer;
149+ color: var(--ink);
150+ transition: border-color 0.2s, background 0.2s;
151+ flex-shrink: 0;
152+ }
153+
154+ .menu-toggle:hover {
155+ border-color: var(--ink);
156+ background: rgba(13, 15, 20, 0.04);
157+ }
158+
159+ .icon-close { display: none; }
160+
161+ .menu-toggle[aria-expanded="true"] .icon-menu { display: none; }
162+ .menu-toggle[aria-expanded="true"] .icon-close { display: block; }
163+
164+ /* ── MOBILE ── */
165+ @media (max-width: 680px) {
166+ #site-header {
167+ grid-template-columns: 1fr auto;
168+ padding: 0 1.25rem;
169+ min-height: 64px;
170+ }
171+
172+ nav {
173+ display: none;
174+ position: absolute;
175+ top: calc(100% + 2px); /* sits just below the border */
176+ left: 0;
177+ right: 0;
178+ flex-direction: column;
179+ gap: 0;
180+ background: var(--paper);
181+ border-bottom: 2px solid var(--ink);
182+ box-shadow: 0 8px 24px rgba(13, 15, 20, 0.08);
183+ }
184+
185+ nav.open {
186+ display: flex;
187+ animation: slideDown 0.2s ease both;
188+ }
189+
190+ @keyframes slideDown {
191+ from { opacity: 0; transform: translateY(-8px); }
192+ to { opacity: 1; transform: translateY(0); }
193+ }
194+
195+ nav a {
196+ padding: 1rem 1.5rem;
197+ border-top: 1px solid var(--border);
198+ font-size: 0.95rem;
199+ letter-spacing: 0.06em;
200+ }
201+
202+ nav a::after { display: none; }
203+
204+ nav a.active {
205+ color: var(--accent);
206+ background: rgba(0, 200, 150, 0.05);
207+ }
208+
209+ .tag-badge { display: none; }
210+ .menu-toggle { display: flex; }
104211 }
105212</style >
213+
214+ <script >
215+ const header = document.getElementById('site-header');
216+ const toggle = document.getElementById('menu-toggle');
217+ const nav = document.getElementById('main-nav');
218+
219+ // Sticky shadow on scroll
220+ window.addEventListener('scroll', () => {
221+ header?.classList.toggle('scrolled', window.scrollY > 8);
222+ }, { passive: true });
223+
224+ // Toggle menu
225+ toggle?.addEventListener('click', () => {
226+ const isOpen = nav?.classList.toggle('open') ?? false;
227+ toggle.setAttribute('aria-expanded', String(isOpen));
228+ toggle.setAttribute('aria-label', isOpen ? 'Close menu' : 'Open menu');
229+ });
230+
231+ // Close on link click
232+ nav?.querySelectorAll('a').forEach(link => {
233+ link.addEventListener('click', () => {
234+ nav.classList.remove('open');
235+ toggle?.setAttribute('aria-expanded', 'false');
236+ toggle?.setAttribute('aria-label', 'Open menu');
237+ });
238+ });
239+
240+ // Close on outside click
241+ document.addEventListener('click', (e) => {
242+ if (!header?.contains(e.target as Node)) {
243+ nav?.classList.remove('open');
244+ toggle?.setAttribute('aria-expanded', 'false');
245+ toggle?.setAttribute('aria-label', 'Open menu');
246+ }
247+ });
248+ </script >
0 commit comments