Reusable accessible design system: tokens (JSON/YAML) + strict a11y template + validator for AI-built websites
  • JavaScript 100%
Find a file
Paragrimm 75e732cacf Make design-system a real tool; ship GP-fitting green palette
- Add scripts/derive-tokens.js: no-dep CLI that derives a full accessible
  palette (primary/secondary/text/background/surface/focusRing/muted, light
  + dark) from one anchor color, computes WCAG contrast for every pair, and
  exits 1 if any text/large pair is below AA (4.5/3.0). Supports --base,
  --from <url> (best-effort brand-color extraction), --secondary, --name,
  --out, --no-write.
- generate-tokens.js now delegates to derive-tokens so JSON/YAML never drift.
- Replace blue default with preset gp-green (base #2f6b4f): calm sage/forest
  green primary + warm terracotta secondary on warm-neutral surfaces, fitting
  a Hausarzt/GP practice. All pairs AA-pass.
- Document referenced old-site colors and the deliberate move away from the
  red/signal brand toward calming green (old-colors.md).
- Rewrite README as tool docs (install/run, CLI, Kirby integration, validator
  as acceptance gate).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 20:22:30 +02:00
examples Add accessible design system: tokens, a11y rulebook, validator, examples 2026-07-13 19:25:09 +02:00
scripts Make design-system a real tool; ship GP-fitting green palette 2026-07-13 20:22:30 +02:00
a11y-template.md Add accessible design system: tokens, a11y rulebook, validator, examples 2026-07-13 19:25:09 +02:00
old-colors.md Make design-system a real tool; ship GP-fitting green palette 2026-07-13 20:22:30 +02:00
README.md Make design-system a real tool; ship GP-fitting green palette 2026-07-13 20:22:30 +02:00
tokens.json Make design-system a real tool; ship GP-fitting green palette 2026-07-13 20:22:30 +02:00
tokens.yaml Make design-system a real tool; ship GP-fitting green palette 2026-07-13 20:22:30 +02:00
validate.js Add accessible design system: tokens, a11y rulebook, validator, examples 2026-07-13 19:25:09 +02:00

a11y-design-system

Ein Werkzeug für barrierefreie Websites: aus einer Ankerfarbe leitet es eine vollständige, WCAG-2.1-AA-geprüfte Farbpalette (Light + Dark) ab, liefert Design-Tokens (JSON/YAML) mit berechneten Kontrastwerten, ein striktes Barrierefreiheits-Regelwerk und einen Validator, der als Abnahme-Gate dient.

Die mitgelieferte Standard-Palette ist gp-green — ruhiges Salbei-/Waldgrün als Primärfarbe, warme Terrakotta als Sekundärakzent, warm-neutrale Flächen: passend für eine Hausarztpraxis (nicht das kühle Klinik-Blau). Wie sie zustande kam: siehe old-colors.md.


Schnellstart

Voraussetzung: Node.js (keine Abhängigkeiten, kein npm install nötig).

# 1. Palette aus einer Ankerfarbe ableiten (schreibt tokens.json + tokens.yaml)
node scripts/derive-tokens.js --base '#2f6b4f' --name gp-green --out tokens.json

# 2. Standard-Palette neu erzeugen (identisch zu oben, One-Liner)
node scripts/generate-tokens.js

# 3. Eine Seite gegen das Regelwerk prüfen (Abnahme-Gate)
node validate.js examples/passing.html   # exit 0 → a11y-ready
node validate.js examples/failing.html   # exit 1 → nicht auslieferbar

Achtung Shell: Das # in --base '#2f6b4f' immer quoten, sonst interpretiert die Shell den Rest als Kommentar.


Das Tool: scripts/derive-tokens.js

Nimmt eine Anker-/Basisfarbe und erzeugt eine vollständige, zugängliche Palette (primary, secondary, text, background, surface, focusRing, muted) für Light und Dark, berechnet für jedes relevante Paar den WCAG-Kontrast und bricht mit Exit-Code 1 ab, wenn ein Text-/Large-Paar unter AA liegt (4.5 / 3.0).

node scripts/derive-tokens.js --base <hex> [Optionen]
node scripts/derive-tokens.js --from <url> [Optionen]
Option Bedeutung
--base <hex> Ankerfarbe, z. B. '#2f6b4f'. Ihr Farbton treibt die Primärfarbe.
--from <url> Best-Effort: lädt eine Seite + verlinkte CSS und nimmt die dominante, gesättigte Markenfarbe als Basis.
--secondary <hex> Optionaler Anker für den Sekundär-Farbton.
--name <preset> Menschlicher Name, landet in meta.preset (z. B. gp-green).
--out <datei> Ausgabepfad JSON (Default: tokens.json). Ein .yaml-Spiegel wird daneben geschrieben.
--no-write Ergebnis nur nach stdout drucken, nichts schreiben.

Wie die Ableitung funktioniert (bewusste Designentscheidungen):

  • Primary behält den Basis-Farbton und wird so weit abgedunkelt (Light) bzw. aufgehellt (Dark), bis sie 4.5:1 auf dem Hintergrund erreicht — Markenton bleibt, Lesbarkeit ist garantiert.
  • Secondary nutzt einen split-komplementären Farbton (Basis + 225°). Bei grüner Basis landet das auf einem warmen, menschlichen Terrakotta/Amber — ruhig und nicht klinisch.
  • Flächen/Text verwenden einen warmen Neutral-Ton (≈ 35°), damit Hintergründe wie warmes Papier wirken statt wie kühles Klinikweiß.
  • Focus-Ring übernimmt die (bereits AA-starke) Primärfarbe → unübersehbar und klar über 3:1.

Beispielausgabe (--base '#2f6b4f' --name gp-green):

 LIGHT
   primary     #2B8058  (vs bg 4.69:1)
   secondary   #B45834  (vs bg 4.63:1)
   text        #3A332A  (vs bg 12.05:1)
   background  #FCFBFA
   surface     #F6F3EE
   focusRing   #2B8058  (vs bg 4.69:1)
   muted       #7A7266  (vs bg 4.59:1)
 DARK
   primary     #3B9169  (vs bg 4.67:1)
   secondary   #C7663F  (vs bg 4.63:1)
   text        #DDD9D5  (vs bg 12.84:1)
   background  #191613
   surface     #2E2A24
   focusRing   #42A476  (vs bg 5.84:1)
   muted       #8C8273  (vs bg 4.77:1)
 → RESULT: PASS — all pairs AA.

scripts/generate-tokens.js ist ein dünner Wrapper darum: er erzeugt die Standard-Palette (gp-green) reproduzierbar, damit JSON und YAML nie auseinanderlaufen.


Die Tokens: tokens.json / tokens.yaml

Bereich Inhalt
color.light.* / color.dark.* primary, secondary, text, background, surface, focusRing, muted — je mit value, contrast_text, contrast_bg, wcag_aa_text, wcag_aa_large
spacing 4-px-Raster
typography Font-Stacks, Body ≥ 16px, Überschriften-Skala
radius none … full
focusRing width: 3px (min 2px), Offset, Stil
minTargetSize 44px (WCAG 2.5.5)

Alle Kontrastwerte sind berechnet (WCAG-2.x-Relativluminanz), nicht geschätzt. Die Ableitung schlägt fehl, sobald ein Paar AA verletzt.


Tokens in ein Website-Projekt (z. B. Kirby) einbinden

  1. tokens.json (und optional tokens.yaml) ins Projekt kopieren.

  2. An genau einer Stelle in CSS-Custom-Properties abbilden — Light + Dark vollständig aus den Tokens:

    :root {
      --color-primary:    #2B8058;  /* color.light.primary */
      --color-secondary:  #B45834;  /* color.light.secondary */
      --color-text:       #3A332A;
      --color-background: #FCFBFA;
      --color-surface:    #F6F3EE;
      --color-focus-ring: #2B8058;
      --color-muted:      #7A7266;
    }
    @media (prefers-color-scheme: dark) {
      :root {
        --color-primary:    #3B9169;  /* color.dark.primary */
        --color-secondary:  #C7663F;
        --color-text:       #DDD9D5;
        --color-background: #191613;
        --color-surface:    #2E2A24;
        --color-focus-ring: #42A476;
        --color-muted:      #8C8273;
      }
    }
    

    Keine weiteren hartkodierten Farben irgendwo. In Kirby z. B. ein Snippet/Partial, das diesen :root-Block ausgibt; alle Templates nutzen nur var(--color-*). Tokens ändern → Palette neu ableiten → dieser eine Block wird neu erzeugt.

  3. a11y-template.md befolgen für jede Seite: Skip-Link zuerst, <header>

    • <nav aria-label>, <main id="content">, <footer>, lang, genau eine <h1>, Labels, Kontrast ≥ 4.5:1 / ≥ 3:1. Das Dokument ist das Abnahmekriterium — vor dem Coden lesen.

Der Validator ist das Abnahme-Gate: validate.js

node validate.js dist/index.html

Prüft: Landmarken (<header>, <main id="content">, <footer>), Skip-Link als erstes fokussierbares Element → #content, <html lang>, genau eine <h1>, und dass hartkodierte Farben den Kontrast gegen den Token-Hintergrund erfüllen. Exit 0 = auslieferbar, alles andere = nicht auslieferbar. Ideal für CI:

- name: a11y gate
  run: |
    for f in dist/**/*.html; do node validate.js "$f"; done

KI-Tools instruieren (Claude Code / Cloud Code etc.)

KI-Generatoren wenden diese Regeln nicht von selbst an — sie müssen es gesagt bekommen. An den Anfang der CLAUDE.md / des System-Prompts:

Lies vor jeder HTML/CSS-Ausgabe a11y-template.md und tokens.json und befolge sie strikt. Nutze nur Farben aus den Tokens (Light und Dark). Jede Seite muss node validate.js <datei> bestehen — ein FAIL ist ein kaputter Build.

Dateien

Datei Zweck
scripts/derive-tokens.js Das Tool: Palette aus einer Ankerfarbe ableiten, AA-Gate
scripts/generate-tokens.js Standard-Palette gp-green reproduzierbar erzeugen
tokens.json / tokens.yaml Design-Tokens mit berechneten Kontrasten
a11y-template.md Striktes Regelwerk — vor dem Coden lesen
validate.js Abnahme-Validator
old-colors.md Referenz: Farben der Bestandsseite + bewusste Neuausrichtung
examples/ Eine bestehende (passing) und eine kaputte (failing) Seite