Design system · colour assignment

Give every stylable asset a considered colour — and prove it passes.

Work top to bottom. Each row is one asset or role you can style with CSS3: note the palette token you assign, tick it when done, and confirm it meets the contrast gate. The order builds the brand from foundations up to how it behaves live — static surfaces first, then every interaction and user state, then the WCAG 2.2 gates that keep it accessible. Ticks and tokens save in this browser.

0 / 126 done

Contrast gates (WCAG 2.2)

  • 4.5:1 Body text vs its background 1.4.3 AA
  • 3:1 Large text (≥24px, or ≥18.66px bold) 1.4.3
  • 3:1 UI boundaries, icons, focus rings, chart marks 1.4.11
  • 7:1 Body target where practical 1.4.6 AAA
  • exempt Disabled controls & pure decoration — keep legible anyway

The 10-state grammar

  • Every interactive asset needs a colour for default, hover, focus-visible, active, loading, success, error, disabled, plus empty/filled and selected/current.
  • Never colour alone (1.4.1): pair every state with a second cue — underline, icon, weight, border, dot or text.
01

Palette foundations — define your roles first

0/12

Before touching a component, name the roles. Everything downstream references these tokens, never a raw hex. Assign one value per role for each theme (dark and light).

✓Asset / roleStylable with CSS3Assign tokenContrast / WCAG
Page background--bg background of <body> / the app canvas The ground every text ratio is measured against. Pick this first.
Surface--surface background of cards, panels, nav, inputs 3:1 boundary vs page if borderless (1.4.11).
Raised / recessed--bg-alt elevated menus, wells, table zebra Distinguish by lightness step, not hue alone.
Text — primary--text body copy, headings 4.5:1 vs every surface it sits on. Aim 7:1.
Text — muted--text-muted captions, helper, metadata Still 4.5:1 (or 3:1 if large). Muted ≠ low-contrast.
Brand — primary (copper)--color-primary logo, emphasis, primary marks Text on it 4.5:1; the mark 3:1 vs page.
Brand — secondary / accent--color-accent active states, focus, links, CTAs Accent shape 3:1 vs page; on-accent text 4.5:1.
Border / divider--surface-border hairlines, input borders, dividers Structural borders that group: 3:1 (1.4.11).
Focus ring--color-accent the visible keyboard focus indicator 3:1 vs adjacent; ≥2px; 2.4.7 + 2.4.13.
Semantic — success / warning / danger(page-scoped) status text, badges, alerts, validation Matched saturation; usable as text (4.5:1) and mark (3:1).
Scrim / overlay--scrim the dim behind modals & drawers Enough dim to separate layers; no text ratio required.
Selection::selection highlighted text colour + background Selected text ≥4.5:1 against the selection background.
02

Global & structural surfaces

0/8

The static skeleton — the colours a page wears before anything is interacted with.

✓Asset / roleStylable with CSS3Assign tokenContrast / WCAG
Body / canvas--bg background, base color, color-scheme Set color-scheme so controls & scrollbars theme correctly.
Section bands alternating background between sections Separate by lightness; add a hairline if 3:1 not met.
Containers / cards--surface background, border, box-shadow Shadow is not a contrast substitute — borderless cards still need 3:1.
Dividers / rules--surface-border border-color, hr Decorative: any contrast. Grouping: 3:1.
Scrollbars scrollbar-color, ::-webkit-scrollbar* Track/thumb visible in both themes; follow color-scheme.
Text selection::selection background + color Legible highlight; test on dark and light grounds.
Skip-to-content link background, color on focus Visible on focus; 4.5:1; first focusable element.
Ambient background decorative gradients, body::before Decorative only — must not drop text below its ratio.
03

Typography & text colour

0/9

Colour applied to words. Each is measured against the surface directly behind it.

✓Asset / roleStylable with CSS3Assign tokenContrast / WCAG
Headings h1–h6--text color 4.5:1 (large sizes may use 3:1, but do not lean on it).
Body copy--text color 4.5:1 minimum; 7:1 target for long-form.
Secondary / lede--text-muted color 4.5:1 — the commonest failure point. Verify, don’t eyeball.
Links (in body)--color-accent color, text-decoration-color 3:1 vs surrounding text and a non-colour cue (underline).
Link states--color-accent hover / visited / focus / active Each distinct; visited still passes; focus ring 3:1.
Inline code / kbd color, background, border Code text 4.5:1 vs its tinted background, not the page.
Blockquote--text-muted color, left border-color Accent bar 3:1 if it carries meaning.
List markers::marker color Decorative exempt; if they encode state, 3:1.
Captions / metadata--text-muted color, tabular-nums Small text still needs 4.5:1.
04

Buttons & calls to action

0/9

Three variants, each across the full state grammar. Colour is never the sole affordance — shape, border and label carry it too.

✓Asset / roleStylable with CSS3Assign tokenContrast / WCAG
Primary--color-accent / --gradient-primary background, color, border On-accent label 4.5:1; fill 3:1 vs page.
Secondary--surface-border border-color, color, background Border 3:1 vs page; label 4.5:1.
Ghost / tertiary color, hover background Needs a visible hover/focus (no resting border).
Destructive--cl-bad outline or fill Red is not the only cue — add wording/icon (1.4.1).
Hover:hover background/border shift Perceivable; keep contrast — don’t lighten to failure.
Focus-visible:focus-visible box-shadow/outline ring 3:1 ring, ≥2px, 3px offset; never remove without replacing.
Active / pressed:active background, subtle transform Distinct from hover; instant (~80ms).
Loading[aria-busy] spinner color, disabled look Announce busy; keep label legible.
Disabled:disabled muted background/color Contrast-exempt, but readable and clearly inert.
05

Forms, inputs & validation

0/12

Where user behaviour meets colour most intensely. Every field state needs a colour and a non-colour signal.

✓Asset / roleStylable with CSS3Assign tokenContrast / WCAG
Label--text color 4.5:1; a real <label>, never placeholder-as-label.
Input surface--surface background, border-color, color Border 3:1 vs page; typed text 4.5:1 vs field.
Placeholder:placeholder-shown ::placeholder color Needs 4.5:1; never carries essential info.
Helper text--text-muted color 4.5:1; describe format before the user errs.
Focus ring:focus-visible border-color, box-shadow 3:1; the ink-stroke / ring must be obvious.
Valid:user-valid border-color, ✓ icon Green + tick, not green alone.
Error:user-invalid border-color, message color Red border + text via aria-describedby (1.4.1, 3.3.1).
Checkbox / radio:checked box background/border, tick Checked mark 3:1; not conveyed by fill colour alone.
Toggle / switch:checked track + thumb background Distinguishable to colour-blind users — add position/label.
Select / dropdown control + option color/background Caret 3:1; open list follows theme.
Range / slider track, filled range, thumb Filled 3:1 vs track; thumb 3:1 vs both.
Disabled / readonly muted background/color Clearly inert; contrast-exempt but readable.
07

Tables, lists & dense data

0/8

High-density surfaces where zebra, hover and selection colours must stay subtle yet perceivable.

✓Asset / roleStylable with CSS3Assign tokenContrast / WCAG
Table header--bg-alt background, color, bottom border Header text 4.5:1; sort affordance not colour-only.
Row / cell color, cell border Body 4.5:1; grouping borders 3:1.
Zebra striping:nth-child alternating row background Lightness step; must not drop text contrast.
Row hover:hover row background Perceivable but calm; text stays legible over it.
Selected row[aria-selected] background + indicator Tint + a check/edge, not tint alone.
Sortable headeraria-sort arrow color, active column Direction shown by icon + text, 3:1.
Sticky header--surface background stays opaque on scroll Opaque so rows don’t bleed through.
Empty / no-data--text-muted placeholder color 4.5:1 guidance; explain the next action.
08

Badges, pills, tags & chips

0/5

Small status objects that lean hardest on colour — so they most need a second signal.

✓Asset / roleStylable with CSS3Assign tokenContrast / WCAG
Status pillsemantic background (tint), color, border Text on tint 4.5:1; add a dot + word.
Neutral tag--surface-border background, color, border 4.5:1; consistent radius with the system.
Removable chip close-icon color, hover Close control 3:1 + ≥24px target (2.5.8).
Count / notification badge--cl-bad background, color On-badge number 4.5:1; not the sole notifier.
Category dot background of the dot If it encodes category, pair with a label.
09

Feedback, notifications & loading

0/7

Transient and persistent messaging — one visual grammar, four severities, always with words.

✓Asset / roleStylable with CSS3Assign tokenContrast / WCAG
Toastsemantic background, color, icon, border Severity by icon + text + colour; content 4.5:1.
Inline alert / bannersemantic tinted background, border, color Icon + heading carry meaning alongside the tint.
Form status linesemantic color of the message Never a bare colour change — always a sentence.
Progress bar--color-accent track + fill background Fill 3:1 vs track; pair with a % or label.
Spinner / loader--color-accent color, border-color Announce busy; visible on the surface.
Skeleton--bg-alt placeholder background, shimmer Subtle; no shimmer under reduced-motion.
Empty state--text-muted illustration + guidance color Guidance 4.5:1; give a next step.
10

Overlays — modals, popovers, tooltips

0/5

Layered surfaces above the page. Each needs its own opaque background and to read against the scrim.

✓Asset / roleStylable with CSS3Assign tokenContrast / WCAG
Modal / dialog--surface background, border, box-shadow Opaque surface; content passes; trap focus.
Backdrop / scrim--scrim background (dim) Separates layer; dialog title 4.5:1 on its surface.
Popover--surface background, border, arrow Edge visible vs page (3:1); actionable content passes.
Tooltip--bg-alt background, color 4.5:1; also satisfy 1.4.13 (dismissable, hoverable).
Context menu--surface item color, hover, separators Hover 3:1; destructive item + word, not red alone.
11

Media, icons & graphics

0/5

Non-text visuals still carry contrast duties when they convey meaning.

✓Asset / roleStylable with CSS3Assign tokenContrast / WCAG
Icons — meaningful--text / --color-accent color / fill 3:1 vs background when they convey info (1.4.11).
Icons — decorative--text-muted color Exempt; mark aria-hidden.
Avatars / initials--color-primary background, initial color Initial 4.5:1 on the generated background.
Image overlays / captions gradient/scrim under text, caption color Text-over-image needs a scrim to hold 4.5:1 everywhere.
Logo / wordmarkbrand on-light and on-dark variants Provide both; holds on every surface it lands on.
12

Data visualisation

0/7

Charts have their own rules — marks against ground, marks against each other, and never hue as the only channel.

✓Asset / roleStylable with CSS3Assign tokenContrast / WCAG
Categorical seriesviz-1…n series fill/stroke Each mark 3:1 vs plot ground; distinguishable by shape/label too.
Sequential / diverging ramp stops Perceptually even; check colour-blind safety.
Axes & gridlines--surface-border stroke, tick color Gridlines may be <3:1 (decorative); axis labels 4.5:1.
Data labels / values--text color 4.5:1 wherever they sit (over bars too).
Tooltip / crosshair--surface background, color Readable over any series.
Legend swatch + label Swatch 3:1; label 4.5:1; not optional for colour coding.
Thresholds / statessemantic good / warn / critical bands Encode with label or pattern in addition to colour.
13

Interaction-state matrix

0/12

The colour every interactive element must define — and the second, non-colour cue that rides alongside it (WCAG 1.4.1). Assign the token; the cue column is the rule.

✓StateSuggestedAssign tokenRequired 2nd cueNote
Default:link / resting base token — Resting colour of the element.
Hover:hover accent / tint shift background / underline appears Pointer only — never the sole way to reach anything.
Focus-visible:focus-visible --color-accent ring 2px ring, 3px offset 3:1 (2.4.7 / 2.4.13). The keyboard user’s lifeline.
Active / pressed:active darker / inset transform / inset shadow Instant feedback (~80ms).
Selected / current[aria-current] / [aria-selected] --color-accent underline / check / bar Programmatic + visual; not colour alone.
Loading[aria-busy] muted + spinner spinner / progress Announce; block re-entry.
Success— --cl-ok icon ✓ + text Green + tick + words.
Error— --cl-bad icon + message Red + text via aria-describedby.
Disabled:disabled muted reduced opacity + cursor Contrast-exempt; still clearly inert.
Empty / filled:placeholder-shown muted vs primary placeholder vs value Distinguish an empty field from a filled one.
Expanded[aria-expanded] --color-accent caret rotation State on the trigger, mirrored in colour + icon.
Drag / drop.dragover accent tint dashed edge / insert line Drop target obvious without colour alone.
14

User & environment preferences

0/7

Colour that must adapt to the person and device — the difference between a brand and an accessible brand.

✓Asset / roleStylable with CSS3Assign tokenContrast / WCAG
Light / dark parityprefers-color-scheme full token set per theme Re-run every contrast check in both themes — don’t assume inversion works.
Explicit theme toggle[data-theme] override tokens both directions Toggle must beat the OS setting in both directions.
Increased contrastprefers-contrast: more stronger borders, less tint Raise border/text contrast; reduce translucency.
Forced coloursforced-colors system palette + SystemColors Windows High Contrast — keep focus + borders, don’t fight it.
Reduced motionprefers-reduced-motion static state changes Colour shift replaces animated transitions.
Print@media print ink-on-white, remove chrome Text legible without the brand background.
Colour-blind safety deuter / prot / trit review No pair relies on red↔green alone; verify with a simulator.
15

WCAG & contrast sign-off

0/12

The gates every colour decision above must clear. Tick each once verified with a tool, not by eye.

✓CriterionRequirementHow to verify
1.4.3 Text contrast Body ≥ 4.5:1 · large ≥ 3:1 Check every text/surface pair with a contrast checker.
1.4.6 Enhanced (AAA) Body ≥ 7:1 where practical Target for long-form reading surfaces.
1.4.11 Non-text contrast UI parts, focus, icons, chart marks ≥ 3:1 Borders, states, meaningful graphics.
1.4.1 Use of colour Never the only signal Every state also has icon / text / shape / underline.
2.4.7 Focus visible Visible keyboard focus on all controls Tab the whole page; nothing invisible.
2.4.11 / 2.4.13 Focus (2.2) Not obscured · adequate appearance Ring not hidden by sticky bars; ≥2px, 3:1.
2.5.8 Target size (2.2) Interactive targets ≥ 24×24px Chip close buttons, icon buttons, table actions.
1.4.10 Reflow Usable at 320px, no 2-D scroll Colour bands must not force horizontal scroll.
1.4.4 Resize text Legible at 200% zoom No fixed-px colour boxes that clip enlarged text.
2.3.3 Animation from interactions Honour reduced-motion Colour transitions become instant.
Both themes verified Light + dark both pass every gate Dark mode is where ratios silently fail.
Colour-blind pass No red/green-only distinctions Simulate deuteranopia, protanopia, tritanopia.