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,94 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import { site, social } from '../lib/data';
|
||||
import Badge from './Badge.astro';
|
||||
import Container from './Container.astro';
|
||||
import ExternalLink from './ExternalLink.astro';
|
||||
|
||||
const year = new Date().getFullYear();
|
||||
const linkClass =
|
||||
'decoration-accent underline-offset-4 hover:underline decoration-2 inline-flex items-center gap-2';
|
||||
const titleClass = 'font-display mb-4 text-sm font-bold tracking-wide uppercase';
|
||||
---
|
||||
|
||||
<footer class="surface-alt border-line border-t">
|
||||
<Container class="grid gap-10 py-14 sm:grid-cols-2 lg:grid-cols-[1.5fr_1fr_1fr_1fr]">
|
||||
<section aria-labelledby="footer-about">
|
||||
<h2 id="footer-about" class="font-display text-3xl font-black tracking-tight uppercase">
|
||||
{site.name}
|
||||
</h2>
|
||||
<p class="mt-3 max-w-sm">{site.footer.about}</p>
|
||||
<Badge variant="solid" icon="tabler:antenna" class="mt-4">
|
||||
{site.author.callsign} · {site.author.locator}
|
||||
</Badge>
|
||||
</section>
|
||||
|
||||
<nav aria-labelledby="footer-links">
|
||||
<h2 id="footer-links" class={titleClass}>
|
||||
{site.footer.quickLinksTitle}
|
||||
</h2>
|
||||
<ul class="flex flex-col gap-2">
|
||||
{site.nav.map((item) => (
|
||||
<li>
|
||||
<a href={item.href} class={linkClass}>
|
||||
{item.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
<li>
|
||||
<a href={site.footer.rss.href} class={linkClass}>
|
||||
<Icon name="tabler:rss" class="size-4" aria-hidden="true" />
|
||||
{site.footer.rss.label}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<nav aria-labelledby="footer-legal">
|
||||
<h2 id="footer-legal" class={titleClass}>
|
||||
{site.footer.legalTitle}
|
||||
</h2>
|
||||
<ul class="flex flex-col gap-2">
|
||||
{site.footer.legal.map((item) => (
|
||||
<li>
|
||||
<a href={item.href} class={linkClass}>
|
||||
{item.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{social.length > 0 && (
|
||||
<section aria-labelledby="footer-social">
|
||||
<h2 id="footer-social" class={titleClass}>
|
||||
{site.footer.socialTitle}
|
||||
</h2>
|
||||
<ul class="flex flex-wrap gap-2">
|
||||
{social.map((s) => (
|
||||
<li>
|
||||
<ExternalLink
|
||||
href={s.href}
|
||||
icon={false}
|
||||
title={s.label}
|
||||
class="border-fg hover:bg-fg hover:text-bg size-11 justify-center border transition-colors"
|
||||
>
|
||||
<Icon name={s.icon} class="size-5" aria-hidden="true" />
|
||||
<span class="sr-only">{s.label}</span>
|
||||
</ExternalLink>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
)}
|
||||
</Container>
|
||||
|
||||
<div class="border-line border-t">
|
||||
<Container class="flex flex-col gap-2 py-5 text-sm sm:flex-row sm:items-center sm:justify-between">
|
||||
<p>
|
||||
© {year} {site.author.name} · {site.footer.license}
|
||||
</p>
|
||||
<p class="font-display font-bold tracking-wide uppercase">{site.footer.signoff}</p>
|
||||
</Container>
|
||||
</div>
|
||||
</footer>
|
||||
Reference in New Issue
Block a user