creatordesign system
GitHub
Accessibility
Foundation · Creator Design System

Accessibility

Not a checklist bolted on at the end — these are tokens and defaults, so a component gets them by existing. Everything below is already active on this page.

Accessibility means the site works for every reader — keyboard-only, screen-reader, low-vision, colour-blind, motion-sensitive, on a cracked phone in sunlight. It is not a compliance checklist bolted on at launch; it is the same craft as design: contrast is legibility, focus order is layout, labels are copywriting. Below is the system's contract — ten points, each demonstrated live, shipped as a foundation file (08-a11y.css) rather than an audit.

1 · Focus — press Tab
A link

Two-tone by design: a halo in the opposite value, then the accent ring. That's what makes one ring readable on white, on near-black and on a photo. Mouse users never see it — :focus-visible, not :focus — and it is never removed without a replacement.

:focus-visible · --focus-ring + --focus-ring-alt · .focus-halo for media surfaces

2 · Skip link — Tab once from the top

The first tab stop on every page jumps past the nav to #main. It's off-screen until focused, then it's a real, styled control — not a 1px sliver.

3 · Screen-reader text

.sr-only for text only assistive tech needs; .sr-only-focusable for things that should appear when tabbed to. An icon-only control always carries an aria-label and its <svg> is aria-hidden. This sentence is only read aloud.

.skip-link · .sr-only · .sr-only-focusable

PairLightDarkVerdict
--fg-default on canvas15.8:116.4:1AAA — body, headings
--fg-muted on canvas7.4:17.1:1AAA — secondary copy
--fg-subtle on canvas4.9:14.6:1AA — labels, the slate voice
--fg-accent on canvas5.6:15.1:1AA — accent text
--fg-on-accent on accent4.6:14.6:1AA — primary buttons
--fg-faint on canvas3.1:13.0:1decorative only — never carries information

4 · Contrast — targets: body ≥ 4.5:1, large text and UI ≥ 3:1, long-form prose aiming at 7:1. Both themes, measured, not assumed.

5 · Target size
32px visually, 44px to your thumb.

.tap-target expands the hit area instead of making the control bigger — the layout stays tight, the target doesn't.

--tap-min 44px · .tap-target

6 · Colour is never alone
✓ Passed ✕ Failed ◐ Editing

Every status carries a word and a glyph as well as a hue, so it survives colour blindness, a greyscale print and a bad projector.

status = colour + text + shape

7 · Motion & forced colours

Under prefers-reduced-motion every animation collapses to a 160ms fade and all loops stop — the reveal still happens, it just stops moving.

Under Windows high-contrast, patterns and scrims disappear and structure survives, because the structure is built from real borders rather than from background images.

@media (prefers-reduced-motion) · @media (forced-colors: active)

8 · Forms say what happened and how to fix it
Add a domain after the @ to finish this address.

The invalid state is driven by aria-invalid and the message is wired with aria-describedby, so the styling and the screen reader can never disagree. Never “Invalid input” — say the fix.

aria-invalid · aria-describedby · .error-text

9 · State lives in ARIA, not in classes

Tabs use aria-selected, nav uses aria-current, toggles use aria-pressed, and the CSS styles those attributes. There is no .is-active anywhere in the system — one source of truth means the visual state and the announced state cannot drift apart.

10 · Print

The resume and any article print as ink on paper: chrome hidden, textures off, link URLs spelled out, cards unbroken across pages.

[aria-selected] · [aria-current] · [aria-pressed] · @media print