Accessibility
Farn targets WCAG 2.1 Level AA. The token system, focus styles, and motion controls are designed to make accessible output the default rather than an afterthought.
WCAG 2.1 Level AA
All semantic foreground/background token pairs are chosen to meet at least 4.5:1 contrast in both light and dark themes. The one exception (--color-text-tertiary) is documented explicitly in the matrix below.
Global focus ring
A 2px focus ring using --color-accent applies to every focusable element without opt-in. It shows only on keyboard navigation — mouse clicks suppress it via:focus:not(:focus-visible).
Reduced motion
A blanket rule in tokens/base.css collapses all animation and transition durations to 0.01 ms when the OS motion preference is set — no per-component guard required.
WCAG contrast matrix
Full contrast matrix for all semantic foreground/background token pairs. Ratings follow WCAG 2.1: AAA ≥ 7:1 · AA≥ 4.5:1 · AA Large ≥ 3:1 (≥ 24 px regular or ≥ 19 px bold) ·Fail < 3:1.
Light mode
| Foreground token | --color-bg | --color-bg-panel | --color-bg-inset |
|---|---|---|---|
--color-text | 17.51:1 AAA | 15.15:1 AAA | 12.50:1 AAA |
--color-text-secondary | 6.99:1 AA | 6.05:1 AA | 4.99:1 AA |
--color-text-tertiary † | 4.47:1 AA Large | 3.87:1 AA Large | 3.19:1 AA Large |
--color-accent | 4.78:1 AA | 4.14:1 AA Large | 3.41:1 AA Large |
--color-accent-hover | 6.91:1 AA | 5.98:1 AA | 4.93:1 AA |
--color-accent-text ‡ | 1.00:1 Fail | 1.16:1 Fail | 1.40:1 Fail |
--color-error | 4.59:1 AA | 3.97:1 AA Large | 3.28:1 AA Large |
--color-warning | 4.59:1 AA | 3.98:1 AA Large | 3.28:1 AA Large |
--color-success | 4.59:1 AA | 3.97:1 AA Large | 3.28:1 AA Large |
Dark mode
| Foreground token | --color-bg | --color-bg-panel | --color-bg-inset |
|---|---|---|---|
--color-text | 17.51:1 AAA | 9.54:1 AAA | 6.99:1 AA |
--color-text-secondary | 15.15:1 AAA | 8.25:1 AAA | 6.05:1 AA |
--color-text-tertiary | 12.50:1 AAA | 6.81:1 AA | 4.99:1 AA |
--color-accent | 9.79:1 AAA | 5.33:1 AA | 3.91:1 AA Large |
--color-accent-hover § | 3.66:1 AA Large | 1.99:1 Fail | 1.46:1 Fail |
--color-accent-text | 17.51:1 AAA | 9.54:1 AAA | 6.99:1 AA |
--color-error § | 3.82:1 AA Large | 2.08:1 Fail | 1.52:1 Fail |
--color-warning § | 3.81:1 AA Large | 2.08:1 Fail | 1.52:1 Fail |
--color-success § | 3.82:1 AA Large | 2.08:1 Fail | 1.52:1 Fail |
† --color-text-tertiary is AA Large only in all light-mode contexts — never use it for body-size text; restrict to ≥ 24 px regular or ≥ 19 px bold.
‡ --color-accent-text (light mode) is designed for text on--color-accent button backgrounds — Fail against page surfaces is by design; both tokens resolve correctly in dark mode (17.51:1 AAA on --color-bg).
§ Dark mode: --color-accent-hover, --color-error,--color-warning, --color-success only meet AA Large on--color-bg; they fail on panel and inset surfaces. Always pair these with a text label or icon — never use colour alone to convey meaning.
Focus styles
Farn ships a global focus ring in tokens/base.css that applies to every focusable element without opt-in:
:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 3px;
border-radius: inherit;
}
:focus:not(:focus-visible) {
outline: none;
}border-radius: inherit means the ring hugs the shape of buttons, inputs, and cards rather than defaulting to a square. Component classes in farn-components.cssinherit this ring automatically — override only when a component needs a custom offset or colour (see --input-focus-shadow in the Forms component).
Reduced motion
tokens/base.css includes a blanket reduced-motion rule:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}The 0.01ms value — rather than 0 — keeps animation events firing so JavaScript listeners do not stall waiting for an animationend event that never comes. Scroll-reveal elements (.scroll-reveal) additionally snap directly to their revealed state, bypassing the observer entirely.
Colour alone
Never use colour as the sole means of conveying information, indicating an action, prompting a response, or distinguishing a visual element (WCAG 1.4.1). Pair every colour-coded signal with at least one of: a text label, an icon, a pattern, or a position cue. Badge variants, for example, combine colour with a text label — the label alone would be sufficient; the colour reinforces but does not replace it.