Centrim Design System — a token-first, accessible React component system.
Accessibility
WCAG 2.2 AA is the baseline every component and reference screen in V1 is built and reviewed against. These are the specific commitments the system makes, and what you must preserve when composing with it.
What is this?
A set of enforced accessibility guarantees: contrast floors on the token layer, a single focus-ring treatment, a 24px minimum pointer target on every control, real semantic elements, and Radix primitives behind every complex interaction.
When to use it
- Adding or changing any interactive component
- Composing a screen out of existing components
- Reviewing a contribution before it enters the system
How to use it
- Build on the semantic element: button, a, label, input, table
- Let Radix own focus trapping, roving focus and dismissal
- Keep every state visible without colour: icon, text, or shape as well
What to avoid
- Clickable divs, placeholder-only labels and hidden focus outlines
- Announcing nothing when content loads, saves or fails
- Targets under 24px, or overlapping hit areas in dense rows
Commitments
| Criterion | Requirement | How the system meets it |
|---|---|---|
| 1.4.3 Contrast (minimum) | 4.5:1 text, 3:1 large text | Semantic text tokens paired with approved backgrounds only |
| 1.4.11 Non-text contrast | 3:1 for controls and boundaries | Border and focus tokens tested in both themes |
| 1.4.1 Use of colour | Colour is never the only cue | Status pairs colour with an icon and text |
| 2.1.1 Keyboard | Everything operable by keyboard | Native elements plus Radix keyboard behaviour |
| 2.4.7 Focus visible | Focus is always visible | One ds-focus-ring treatment on the dedicated focus token |
| 2.4.11 Focus not obscured | Focus stays visible when scrolled | Sticky bars offset scroll, overlays trap focus |
| 2.5.8 Target size (minimum) | 24×24px minimum | ds-target expands hit areas to 24px, 44px on coarse pointers |
| 3.3.2 Labels or instructions | Every field is labelled | FormField owns label, hint and error wiring |
| 4.1.3 Status messages | Changes are announced | Toast, Banner and inline errors use live regions |
Target size
ds-target is how the 24px minimum is met
Small controls — checkboxes, radios, switch knobs, small buttons — are visually under 24px by design. The ds-target utility adds a pseudo-element that expands the hit area to at least 24×24px, and to 44×44px on coarse pointers, without changing the visual dimensions.
If you build a new small control, apply ds-target and keep at least 8px between neighbouring targets so the expanded areas do not overlap.
Forms
Every field has a visible label
Placeholders are hints, not labels. Every input, select, combobox and picker in this system renders a visible label by default — including DateRangePicker, whose label prop is required. Use hideLabel only when an adjacent visible label already names the control, and it still exposes the name to assistive technology.
Errors are announced, associated with the field via aria-describedby, and never communicated by red border alone.
Do & don't
Do
- Give icon-only controls a label and a Tooltip.
- Keep a logical DOM order so tab order needs no tabindex.
- Return focus to the trigger when an overlay closes.
- Provide a data-table alternative for every chart.
Don't
- Do not remove outlines; the focus ring is a system commitment.
- Do not use aria-label to paper over a missing visible label.
- Do not trap keyboard users in a component without an Escape route.
- Do not rely on hover to reveal essential information.
Why it is designed this way
Accessibility is enforced at the token and component layer rather than reviewed screen-by-screen, because composed screens inherit whatever the primitives guarantee. That is why contrast lives in tokens, focus lives in one utility, and target size lives in ds-target: a composer cannot accidentally opt out.