test: vitest unit + playwright e2e suites; fix a11y, contrast, no-JS reveal

- vitest: dictionary parity, slug-map bijection, content-schema validation, JSON-LD (23)
- playwright: both locales, language-switch mapping (incl. no-JS), CV downloads,
  form happy-path + honeypot, axe a11y on 5 templates x 2 themes (23)
- fix: raise ink-faint + light accent to meet WCAG AA 4.5:1 (axe-verified)
- fix: gate reveal animations behind html.js so content is visible without JS
  (progressive enhancement - was hidden at opacity 0); scan reduced-motion path
- fix: validate contact form before the anti-bot time-trap (no false success)
- chore: ESLint node globals, typed diagram props, resvg fontFiles, prettier pass

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
cesnimda
2026-07-04 06:14:18 +02:00
parent 9fa16b650d
commit 033c9ec315
64 changed files with 1627 additions and 278 deletions
+22 -14
View File
@@ -22,21 +22,21 @@ const nav: { id: PageId; label: string }[] = [
];
---
<footer class="mt-24 border-t border-line bg-surface-1">
<footer class="border-line bg-surface-1 mt-24 border-t">
<div class="mx-auto grid max-w-[--content-max] gap-10 px-6 py-14 sm:grid-cols-2 lg:grid-cols-4">
<div class="lg:col-span-2">
<p class="font-display text-h4 font-semibold text-ink">Connor Babbington</p>
<p class="mt-2 max-w-sm text-small text-ink-muted">{d.footer.tagline}</p>
<p class="mt-1 max-w-sm text-small text-ink-muted">{d.footer.availability}</p>
<p class="font-display text-h4 text-ink font-semibold">Connor Babbington</p>
<p class="text-small text-ink-muted mt-2 max-w-sm">{d.footer.tagline}</p>
<p class="text-small text-ink-muted mt-1 max-w-sm">{d.footer.availability}</p>
</div>
<nav aria-label={d.a11y.primaryNav}>
<p class="mono-label mb-3">{d.nav.projects}</p>
<ul class="flex flex-col gap-2 text-small text-ink-muted">
<ul class="text-small text-ink-muted flex flex-col gap-2">
{
nav.map((item) => (
<li>
<a class="transition-colors hover:text-ink" href={pathFor(item.id, locale)}>
<a class="hover:text-ink transition-colors" href={pathFor(item.id, locale)}>
{item.label}
</a>
</li>
@@ -47,33 +47,41 @@ const nav: { id: PageId; label: string }[] = [
<div>
<p class="mono-label mb-3">Contact</p>
<ul class="flex flex-col gap-2 font-mono text-mono-meta text-ink-muted">
<ul class="text-mono-meta text-ink-muted flex flex-col gap-2 font-mono">
<li>
<a class="transition-colors hover:text-ink" href={`mailto:${p.links.email}`}
<a class="hover:text-ink transition-colors" href={`mailto:${p.links.email}`}
>{p.links.email}</a
>
</li>
<li><a class="transition-colors hover:text-ink" href={p.links.linkedin} rel="me noopener">LinkedIn ↗</a></li>
<li>
<a class="transition-colors hover:text-ink" href={p.links.gitea} rel="me noopener"
<a class="hover:text-ink transition-colors" href={p.links.linkedin} rel="me noopener"
>LinkedIn ↗</a
>
</li>
<li>
<a class="hover:text-ink transition-colors" href={p.links.gitea} rel="me noopener"
>git.cesnimda.uk ↗</a
>
</li>
<li><a class="transition-colors hover:text-ink" href={pathFor('cv', locale)}>{d.cv.download}</a></li>
<li>
<a class="hover:text-ink transition-colors" href={pathFor('cv', locale)}
>{d.cv.download}</a
>
</li>
</ul>
</div>
</div>
<div
class="mx-auto flex max-w-[--content-max] flex-col items-start justify-between gap-4 border-t border-line px-6 py-6 sm:flex-row sm:items-center"
class="border-line mx-auto flex max-w-[--content-max] flex-col items-start justify-between gap-4 border-t px-6 py-6 sm:flex-row sm:items-center"
>
<p class="font-mono text-mono-meta text-ink-faint">
<p class="text-mono-meta text-ink-faint font-mono">
{interpolate(d.footer.copyright, { year })}
</p>
<div class="flex items-center gap-4">
<a
href={pathFor('colophon', locale)}
class="font-mono text-mono-meta text-ink-muted transition-colors hover:text-ink"
class="text-mono-meta text-ink-muted hover:text-ink font-mono transition-colors"
>
{d.footer.colophon}
</a>