feat: add design system, theme toggle, SEO head and base layout
- Huemint Magazine tokens for light/dark themes with AA-safe accent variants - FOUC-free theme init + toggle (localStorage, follows OS until chosen) - Seo component (canonical, OG, Twitter, WebSite/Person JSON-LD) - Header with popover mobile menu, 4-column footer with social links - Container, GridLines, SectionHeading, Button, Badge, Card, ExternalLink - site.json / social.json validated with zod - dev-only /styleguide preview Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import { site } from '../lib/data';
|
||||
import Container from './Container.astro';
|
||||
import ThemeToggle from './ThemeToggle.astro';
|
||||
|
||||
const path = Astro.url.pathname;
|
||||
const isActive = (href: string) => (href === '/' ? path === '/' : path.startsWith(href));
|
||||
---
|
||||
|
||||
<header class="border-line relative z-20 border-b">
|
||||
<Container class="flex h-16 items-center justify-between gap-6 md:h-20">
|
||||
<a
|
||||
href="/"
|
||||
class="font-display text-2xl font-black tracking-tight uppercase"
|
||||
aria-label={`${site.name} — ${site.ui.home}`}
|
||||
>
|
||||
{site.name}
|
||||
</a>
|
||||
|
||||
<div class="flex items-center gap-2 md:gap-6">
|
||||
<nav aria-label={site.ui.mainNav} class="hidden md:block">
|
||||
<ul class="flex items-center gap-6">
|
||||
{site.nav.map((item) => (
|
||||
<li>
|
||||
<a
|
||||
href={item.href}
|
||||
aria-current={isActive(item.href) ? 'page' : undefined}
|
||||
class="font-display hover:border-accent aria-[current=page]:border-accent block border-b-3 border-transparent py-1 text-sm font-bold tracking-wide uppercase transition-colors"
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<ThemeToggle />
|
||||
|
||||
<button
|
||||
type="button"
|
||||
popovertarget="mobile-nav"
|
||||
aria-label={site.ui.menuOpen}
|
||||
class="text-fg hover:text-accent inline-flex size-10 cursor-pointer items-center justify-center md:hidden"
|
||||
>
|
||||
<Icon name="tabler:menu-2" class="size-7" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
<!-- Native popover: Escape / light-dismiss / focus handling without custom JS. -->
|
||||
<nav
|
||||
id="mobile-nav"
|
||||
popover
|
||||
aria-label={site.ui.mainNav}
|
||||
class="bg-bg-alt text-fg fixed inset-0 m-0 h-dvh max-h-none w-full max-w-none p-0 md:hidden"
|
||||
>
|
||||
<Container class="flex h-16 items-center justify-between">
|
||||
<span class="font-display text-2xl font-black tracking-tight uppercase" aria-hidden="true">
|
||||
{site.name}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
popovertarget="mobile-nav"
|
||||
popovertargetaction="hide"
|
||||
aria-label={site.ui.menuClose}
|
||||
class="hover:text-accent inline-flex size-10 cursor-pointer items-center justify-center"
|
||||
>
|
||||
<Icon name="tabler:x" class="size-7" aria-hidden="true" />
|
||||
</button>
|
||||
</Container>
|
||||
<Container>
|
||||
<ul class="mt-8 flex flex-col gap-2">
|
||||
{site.nav.map((item) => (
|
||||
<li>
|
||||
<a
|
||||
href={item.href}
|
||||
aria-current={isActive(item.href) ? 'page' : undefined}
|
||||
class="headline aria-[current=page]:decoration-accent block py-2 text-4xl decoration-4 underline-offset-8 aria-[current=page]:underline"
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Container>
|
||||
</nav>
|
||||
</header>
|
||||
Reference in New Issue
Block a user