Accessibility

beta

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 AA commitment

All semantic foreground/background token pairs are chosen to meet at least 4.5:1 contrast (AA for normal text) in both light and dark themes. One exception: --in3-ash (Iron Night's lightest shade, used in light mode as tertiary text and border colour) achieves 3:1 against light backgrounds — AA-large only (18pt+ or 14pt+ bold). Avoid using Ash for body copy or labels below that size.

The full contrast matrix — every semantic pair in both themes, with AA / AAA / fail markings — is on Styles › Color › Accessibility.

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;
}

:focus-visible shows the ring only for keyboard and sequential navigation; mouse clicks suppress it via :focus:not(:focus-visible). This gives keyboard users a clear indicator without adding visual noise for pointer users.

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.css inherit 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;
  }
}

This covers all CSS animations and transitions globally. 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 in site/src/styles/site.css, bypassing the observer entirely for users who prefer reduced motion.

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.