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:
2026-10-16 00:47:50 +03:00
parent c5e0b009ca
commit d8a79ccbe3
19 changed files with 915 additions and 36 deletions
+88
View File
@@ -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>