One token set, two themes
Every colour in CAMARON comes from a semantic token — background, foreground, primary, muted-foreground, border, destructive and their pairs — defined once in the global stylesheet and redefined for the dark theme. Components never hardcode a colour.
This is an accessibility decision as much as a design one. When a component writes a literal colour, it works in exactly one theme and silently fails in the other. Because every value flows through tokens, fixing a contrast problem at the token level fixes it everywhere at once — and, just as importantly, a regression is visible in one file rather than scattered across hundreds of components.
The ratios we target
- 4.5:1 — body text and any text below 18.66px (or below 24px if bold).
- 3:1 — large text, and the visual boundaries of interactive controls such as inputs, focus rings and toggles.
- 3:1 — meaningful graphics: chart series, status glyphs, icon-only controls.
Two things routinely break these ratios in practice, and both are banned in review: faded utility classes that drop a token to a fraction of its opacity, and muted foreground text placed on an already-muted surface. Both look fine to a designer on a good monitor and fail for everyone else.
The automated contrast audit
Contrast is gated in two independent places, because each catches what the other misses:
- Token-level audit. A unit test parses the global stylesheet, resolves every declared colour pair in both light and dark themes, computes the contrast ratio, and fails if any pair falls under its threshold. This catches problems before anything renders.
- Runtime audit. An end-to-end check loads Tier-1 routes in both themes and measures actual computed styles on rendered text. This catches the cases the token audit cannot see — opacity applied at the component level, text over an image or gradient, overlay scrims.
On top of that, a Lighthouse accessibility gate holds every Tier-1 route at or above a 95/100 floor on both desktop and mobile presets, and a visual-regression sweep snapshots key surfaces in light and dark so an unintended theme change is visible in review.
Never colour alone
Roughly one in twelve men has some form of colour vision deficiency, and the field conditions this platform is used in — bright sunlight, a scratched phone screen, a cheap monitor — degrade colour discrimination for everyone.
So colour is always a redundant signal:
- Alert severity carries a glyph and a word, not just red or amber.
- Water-quality status chips state the parameter and the reading alongside the colour.
- Form errors show text and an icon, and are linked to their field programmatically.
- Chart series are distinguishable by shape and label, not hue alone.
High contrast and reduced motion
The accessibility hub exposes toggles for high contrast and reduced motion, persisted locally so they survive a reload. The platform also honours the operating system's prefers-reduced-motion and prefers-contrast settings without the user needing to find those toggles at all.
Reduced motion is treated as a hard stop rather than a slowdown: transitions collapse, auto-rotating content pins in place, and background animation stops. Motion-triggered vestibular disorders are not addressed by making an animation shorter.
Theme choice follows the account when signed in, so a user who needs a specific theme does not have to reconfigure it on every device.
Known contrast limits
- Chart interiors. Dense multi-series charts can place adjacent series close in luminance. The tabular alternative is the reliable path.
- Map tiles. Base map imagery comes from a third party and is not contrast-controlled by us; overlays and labels are.
- Generated PDFs. Export styling is checked less rigorously than the on-screen interface, and PDF/UA conformance is still open.
- Third-party embeds. Payment checkout renders in the provider's own styling.