Contrast: APCA, not WCAG
The stated standard is APCA 3.0 — the Advanced Perceptual Contrast Algorithm — rather than the WCAG 2.x contrast ratio. This is not a preference. The two models disagree, and where they disagree the perceptual one is the one this system follows.Why the difference matters
Sodalite’s publisheddarkHex is #3D5AFE. Against a near-black background it passes a
WCAG ratio check and scores APCA Lc -25.8 — below even the Lc 30 floor for non-text UI.
The ratio says fine; the perceptual model says unreadable; the perceptual model is right.
A ratio check will also mislead you about polarity. It is not polarity-aware, so it tests a
dark-mode colour against a light background and demands a threshold on both — and satisfying
that pushes both values to mid-tone and serves neither theme.
Testing a new pairing
- Test in both light and dark.
- Use an APCA calculator, not a WCAG ratio checker.
- Check the pairings that actually ship —
--muted-foregroundon--muted,--foregroundon--card— not only foreground on background.
mzizi_check_accessibility, which covers contrast,
relative luminance and colour-blindness simulation in one call. See
the MCP server.
Touch targets
56px default, 48px minimum. The minimum is a floor, not a target, and nothing interactive goes below it.Keyboard navigation
Interactive components use Radix UI primitives, which handle:- Focus management — trapping focus in modals, restoring it on close.
- Arrow-key navigation — within menus, tabs, radio groups and comboboxes.
- Escape to close — dialogs, dropdowns, popovers, sheets.
- Enter and Space to activate — buttons, checkboxes, toggles.
- Home and End — first and last item in a list.
Focus indicators
Every focusable element shows a visible ring, 2px with a 2px offset, using the--ring token
(cobalt):
Screen readers
Semantic HTML
Use the element that means what you intend:<button> for actions rather than a div with an
onClick, <a> for navigation, <nav> and <main> as landmarks, and heading levels in
order with none skipped.
ARIA
Radix components carry the ARIA attributes they need. For your own:LiveRegion from the harness rather than building their own — see
placing a component.
Testing
Test with at least one screen reader: VoiceOver on macOS and iOS, NVDA on Windows, TalkBack on Android.Colour independence
Never use colour as the sole carrier of meaning. Pair it with a text label (“Error: invalid email”, not a red border alone), an icon, or a pattern. The mineral palette was chosen with common colour-vision deficiencies in mind, which reduces the problem but does not remove it — two minerals that are distinguishable to you may not be to a reader.Checklist
- Contrast meets the APCA threshold for the text size, in both themes
- Touch targets are 56px default and never below 48px
- Every interaction is reachable by keyboard
- Focus indicators are visible and unmodified
- A screen reader announces the component’s purpose and state
- Colour is not the only signal
- Heading levels are sequential