Centrim Design System — a token-first, accessible React component system.
V2 audit — Inputs & dropdowns
One control chrome, many value types. Every field below shares the same border, height, focus ring, label rhythm and message slot — only the value type and affordance change.
Input
The base single-line field, and the shared chrome for every typed variant (email, URL, date, time, month, week).
InputLive component
Wing letter followed by the room number.
Enter a contact name.
Verified two minutes ago.
Legacy mapping
Legacy forms had four input heights and inconsistent error placement. V2 fixes three heights and one message slot below the control.
| Attribute | Specification |
|---|---|
| Default | size="medium", type="text" |
| Variants | text · with leading/trailing icon · with helper text · with tooltip · error · success |
| Sizes | small (32px) · medium (40px) · large (48px) |
| States | default · hover · focus-visible · filled · disabled · readonly · required · error · success |
| Icon behaviour | Decorative icons at `--size-icon-sm`, `text-text-tertiary`, inset 12px; input padding shifts to 40px on that side. |
| Spacing | Field gap 6px between label, control and message; control padding 12px. |
| Typography | Label `text-label-sm`, value `text-body-sm|md`, messages `text-caption`. |
| Border radius | `rounded-md`. |
| Colour usage | `border-border-default` → `border-border-focus`; error `border-error`, success `border-success`. |
| Responsive | Fills its container; labels wrap rather than truncate. |
When to use
- Any short free-text or typed scalar value
When not to use
- Multi-line — use Textarea
- Choosing from known values — use Select
Accessibility
- Label always rendered (visually hidden is a deliberate exception).
- `aria-invalid` + `aria-describedby` wire the error to the control; errors also carry an icon.
- Tooltip content is supplementary only — required information lives in helper text.
- Visible `ds-focus-ring` on `:focus-visible`; never focus-suppressed.
PasswordInput
Password entry with a reveal toggle.
PasswordInputLive component
At least 12 characters.
Passwords do not match.
Legacy mapping
Legacy password fields had no reveal; V2 standardizes a labelled toggle.
| Attribute | Specification |
|---|---|
| Default | size="medium", masked |
| Variants | masked · revealed |
| Sizes | small · medium · large |
| States | default · focus-visible · error · disabled |
| Icon behaviour | Eye / EyeOff IconButton at the trailing edge, labelled "Show password" / "Hide password". |
| Spacing | Shared Input spacing; toggle inset 4px. |
| Typography | Shared with Input. |
| Border radius | `rounded-md`. |
| Colour usage | Shared with Input. |
| Responsive | Shared with Input. |
When to use
- Sign-in, password change, credential entry
When not to use
- One-time codes — use a dedicated numeric field
Accessibility
- Toggle is a real labelled button and announces the current mode.
- Pointer target ≥24px (≥44px on coarse pointers) via `ds-target`.
Typed inputs
Native-typed fields for email, URL, date, date-time, time, month and week values.
EmailInputUrlInputDateInputDateTimeInputTimeInputMonthInputWeekInputLive component
Legacy mapping
Legacy screens used plain text inputs with regex hints for all of these. V2 uses the native types so mobile keyboards, pickers and validation come for free.
| Attribute | Specification |
|---|---|
| Default | size="medium", with the type's default affordance icon |
| Variants | email · url · date · datetime-local · time · month · week |
| Sizes | small · medium · large |
| States | Identical to Input |
| Icon behaviour | Each type ships a default affordance icon (AtSign, Link2, Calendar, Clock, CalendarRange); override with `leadingIcon` / `trailingIcon`. |
| Spacing | Shared with Input. |
| Typography | Shared with Input. |
| Border radius | `rounded-md`. |
| Colour usage | Shared with Input. |
| Responsive | Native pickers render as platform sheets on mobile. |
When to use
- Dense forms and bulk data entry
When not to use
- When a visible calendar aids the decision — use DatePicker
Accessibility
- Native types give assistive tech the right role and keyboard.
- Format expectations belong in helper text, not the placeholder.
SearchInput
Query entry with search affordance and clear control.
SearchInputLive component
Legacy mapping
Legacy search boxes varied per screen; V2 standardizes icon, placeholder and clear behaviour.
| Attribute | Specification |
|---|---|
| Default | size="medium" |
| Variants | inline · toolbar |
| Sizes | small · medium · large |
| States | empty · typing · has value (clearable) · disabled |
| Icon behaviour | Leading Search icon; trailing labelled clear control appears only when there is a value. |
| Spacing | Shared with Input. |
| Typography | Shared with Input. |
| Border radius | `rounded-md`. |
| Colour usage | Shared with Input. |
| Responsive | Expands full width in toolbars below `md`. |
When to use
- Filtering a list or table
- Global search entry
When not to use
- Selecting from a fixed short list — use Select
Accessibility
- `type="search"` with a real label or `aria-label`; clear control is named.
NumberInput
Numeric entry with optional stepper.
NumberInputLive component
Between 1 and 12.
Legacy mapping
Legacy quantity fields used text inputs with custom +/- markup per screen.
| Attribute | Specification |
|---|---|
| Default | size="medium", stepper visible |
| Variants | plain · with stepper · with unit suffix |
| Sizes | small · medium · large |
| States | default · focus-visible · min/max reached · disabled · error |
| Icon behaviour | Plus / Minus at `--size-icon-xs` inside labelled step controls. |
| Spacing | Shared with Input; stepper column 32/40px wide. |
| Typography | Value `text-body-md`, tabular where alignment matters. |
| Border radius | `rounded-md`. |
| Colour usage | Shared with Input. |
| Responsive | Stepper stays visible; field shrinks first. |
When to use
- Quantities, limits, durations
When not to use
- Approximate values — use RangeInput or Slider
Accessibility
- Step buttons are labelled ("Increase quantity"); the input remains the value source of truth.
- Pointer target ≥24px (≥44px on coarse pointers) via `ds-target`.
RangeInput / Slider
Coarse value selection along a scale.
RangeInputSliderLive component
Legacy mapping
Legacy sliders were unstyled native ranges. V2 keeps a token-styled native range and a Radix Slider for multi-thumb cases.
| Attribute | Specification |
|---|---|
| Default | RangeInput min=0 max=100, value shown |
| Variants | single value · range (Slider) |
| Sizes | Single 24px track row |
| States | default · focus-visible · dragging · disabled |
| Icon behaviour | No icons; the value read-out replaces them. |
| Spacing | Field gap 6px; value read-out aligned to the label baseline. |
| Typography | Read-out `text-label-sm`. |
| Border radius | `rounded-full`. |
| Colour usage | `accent-primary`; disabled falls back to `border-strong`. |
| Responsive | Full width; read-out stays on the label row. |
When to use
- Opacity, volume, coarse thresholds with instant preview
When not to use
- Exact values — use NumberInput
Accessibility
- Keyboard arrow support is native; the numeric value is always visible, not hover-only.
- Visible `ds-focus-ring` on `:focus-visible`; never focus-suppressed.
ColorInput
Colour value selection with a visible hex read-back.
ColorInputLive component
Used on the resident portal header.
Legacy mapping
Legacy branding screens used a bare native swatch with no value text.
| Attribute | Specification |
|---|---|
| Default | size="medium" |
| Variants | swatch + value |
| Sizes | small (32px) · medium (40px) · large (48px) |
| States | default · focus-visible · disabled · error |
| Icon behaviour | None; the swatch is the affordance. |
| Spacing | gap-3 between swatch and value. |
| Typography | Value in `--ds-font-mono`, `text-body-sm`, uppercase. |
| Border radius | `rounded-md` outer, `rounded-sm` swatch. |
| Colour usage | Chrome is neutral; the only saturated colour is the user's value. |
| Responsive | Swatch keeps its size; the value wraps below on narrow widths. |
When to use
- Branding and theming settings
When not to use
- Choosing from a fixed palette — use SelectionCard or ToggleGroup
Accessibility
- The hex value is rendered as text so the choice is never colour-only.
Textarea
Multi-line free text with optional character counter.
TextareaLive component
Give a reason for the change.
Legacy mapping
Legacy notes fields had no counter and inconsistent min-heights.
| Attribute | Specification |
|---|---|
| Default | 3 rows, resize-y |
| Variants | plain · with counter · auto-grow |
| Sizes | rows-driven |
| States | default · focus-visible · disabled · readonly · error · over limit |
| Icon behaviour | No inline icons; error messaging carries the AlertCircle glyph. |
| Spacing | Padding 12px, field gap 6px. |
| Typography | `text-body-md`; counter `text-caption`. |
| Border radius | `rounded-md`. |
| Colour usage | Shared with Input; the counter turns `text-status-error` past the limit. |
| Responsive | Full width; vertical resize only, never horizontal. |
When to use
- Notes, descriptions, messages
When not to use
- Rich text — that is a separate editor, out of V2 scope
Accessibility
- Counter is `aria-live="polite"`; the limit is stated in helper text.
Select
Single choice from a known list.
SelectSelectRootSelectTriggerSelectContentSelectItemLive component
Choose a priority.
Legacy mapping
Legacy screens mixed native selects and custom dropdowns with different heights and no keyboard typeahead.
| Attribute | Specification |
|---|---|
| Default | size="medium", placeholder shown |
| Variants | options list · grouped options |
| Sizes | small · medium · large |
| States | placeholder · selected · open · focus-visible · disabled · error |
| Icon behaviour | ChevronDown at `--size-icon-sm`; Check marks the selected item. |
| Spacing | Trigger padding 12px; item padding 6px/8px. |
| Typography | Trigger `text-body-sm|md`; group labels `text-label-sm`. |
| Border radius | `rounded-md` trigger, `rounded-sm` items. |
| Colour usage | Menu surface `bg-surface` with `shadow-lg`; highlighted item `bg-surface-hover`. |
| Responsive | Menu width matches the trigger; long labels wrap in the list. |
When to use
- 6+ known options where only one applies
When not to use
- Free text — use Combobox
- ≤5 options that should stay visible — use Radio
Accessibility
- Radix Select: typeahead, arrow keys, Escape close, correct ARIA.
- Visible `ds-focus-ring` on `:focus-visible`; never focus-suppressed.
MultiSelect / Combobox
Searchable selection: Combobox for one value, MultiSelect for many.
MultiSelectComboboxLive component
Legacy mapping
Legacy tag/assignee pickers were bespoke per module. V2 provides one searchable pattern.
| Attribute | Specification |
|---|---|
| Default | size="medium", search enabled |
| Variants | single (Combobox) · multiple (MultiSelect) |
| Sizes | medium |
| States | empty · searching · no results · selected · disabled · error |
| Icon behaviour | ChevronDown on the trigger, Check on selected rows, X on removable pills. |
| Spacing | Pill gap 6px; list item padding 6px/8px. |
| Typography | Pills `text-label-sm`; options `text-body-sm`. |
| Border radius | `rounded-md` trigger, `rounded-full` pills. |
| Colour usage | Pills on `bg-background-subtle`; the selected mark uses `text-text-link`. |
| Responsive | Pills wrap and grow the trigger height. |
When to use
- Long option lists, tagging, assignment
When not to use
- Short lists — use Select or CheckboxGroup
Accessibility
- Listbox semantics with `aria-multiselectable`; every remove control is named.
DatePicker / TimePicker
Calendar- and clock-assisted date, range and time selection.
DatePickerDateRangePickerTimePickerLive component
Reviews run every 90 days.
Legacy mapping
Legacy date ranges appeared without visible labels and with three calendar styles. V2 requires a visible label by default and one calendar.
| Attribute | Specification |
|---|---|
| Default | size="medium", visible label |
| Variants | single date · date range · time |
| Sizes | small · medium · large |
| States | empty · selected · open · disabled · error |
| Icon behaviour | Calendar / Clock leading icon at `--size-icon-sm`; navigation uses labelled IconButtons. |
| Spacing | Calendar cell grid on the 4px scale; popover padding 12px. |
| Typography | Trigger `text-body-sm`; calendar cells `text-body-sm`. |
| Border radius | `rounded-md` trigger, `rounded-sm` cells. |
| Colour usage | Selected day `bg-primary`; range fill `bg-primary-subtle`. |
| Responsive | Popover flips and narrows; range calendar stacks to one month below `sm`. |
When to use
- Date decisions helped by seeing the calendar
When not to use
- Bulk entry of known dates — use DateInput
Accessibility
- Label is mandatory (`hideLabel` visually hides but keeps it announced); grid keyboard navigation.