Design 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

V2

The base single-line field, and the shared chrome for every typed variant (email, URL, date, time, month, week).

Input

Live component

Wing letter followed by the room number.

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.

Input — standardized specification
AttributeSpecification
Defaultsize="medium", type="text"
Variantstext · with leading/trailing icon · with helper text · with tooltip · error · success
Sizessmall (32px) · medium (40px) · large (48px)
Statesdefault · hover · focus-visible · filled · disabled · readonly · required · error · success
Icon behaviourDecorative icons at `--size-icon-sm`, `text-text-tertiary`, inset 12px; input padding shifts to 40px on that side.
SpacingField gap 6px between label, control and message; control padding 12px.
TypographyLabel `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`.
ResponsiveFills 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

V2

Password entry with a reveal toggle.

PasswordInput

Live component

At least 12 characters.

Legacy mapping

Legacy password fields had no reveal; V2 standardizes a labelled toggle.

PasswordInput — standardized specification
AttributeSpecification
Defaultsize="medium", masked
Variantsmasked · revealed
Sizessmall · medium · large
Statesdefault · focus-visible · error · disabled
Icon behaviourEye / EyeOff IconButton at the trailing edge, labelled "Show password" / "Hide password".
SpacingShared Input spacing; toggle inset 4px.
TypographyShared with Input.
Border radius`rounded-md`.
Colour usageShared with Input.
ResponsiveShared 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

V2

Native-typed fields for email, URL, date, date-time, time, month and week values.

EmailInputUrlInputDateInputDateTimeInputTimeInputMonthInputWeekInput

Live 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.

Typed inputs — standardized specification
AttributeSpecification
Defaultsize="medium", with the type's default affordance icon
Variantsemail · url · date · datetime-local · time · month · week
Sizessmall · medium · large
StatesIdentical to Input
Icon behaviourEach type ships a default affordance icon (AtSign, Link2, Calendar, Clock, CalendarRange); override with `leadingIcon` / `trailingIcon`.
SpacingShared with Input.
TypographyShared with Input.
Border radius`rounded-md`.
Colour usageShared with Input.
ResponsiveNative 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

V2

Query entry with search affordance and clear control.

SearchInput

Live component

Legacy mapping

Legacy search boxes varied per screen; V2 standardizes icon, placeholder and clear behaviour.

SearchInput — standardized specification
AttributeSpecification
Defaultsize="medium"
Variantsinline · toolbar
Sizessmall · medium · large
Statesempty · typing · has value (clearable) · disabled
Icon behaviourLeading Search icon; trailing labelled clear control appears only when there is a value.
SpacingShared with Input.
TypographyShared with Input.
Border radius`rounded-md`.
Colour usageShared with Input.
ResponsiveExpands 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

V2

Numeric entry with optional stepper.

NumberInput

Live component

Between 1 and 12.

Legacy mapping

Legacy quantity fields used text inputs with custom +/- markup per screen.

NumberInput — standardized specification
AttributeSpecification
Defaultsize="medium", stepper visible
Variantsplain · with stepper · with unit suffix
Sizessmall · medium · large
Statesdefault · focus-visible · min/max reached · disabled · error
Icon behaviourPlus / Minus at `--size-icon-xs` inside labelled step controls.
SpacingShared with Input; stepper column 32/40px wide.
TypographyValue `text-body-md`, tabular where alignment matters.
Border radius`rounded-md`.
Colour usageShared with Input.
ResponsiveStepper 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

V2

Coarse value selection along a scale.

RangeInputSlider

Live component

60%
30

Legacy mapping

Legacy sliders were unstyled native ranges. V2 keeps a token-styled native range and a Radix Slider for multi-thumb cases.

RangeInput / Slider — standardized specification
AttributeSpecification
DefaultRangeInput min=0 max=100, value shown
Variantssingle value · range (Slider)
SizesSingle 24px track row
Statesdefault · focus-visible · dragging · disabled
Icon behaviourNo icons; the value read-out replaces them.
SpacingField gap 6px; value read-out aligned to the label baseline.
TypographyRead-out `text-label-sm`.
Border radius`rounded-full`.
Colour usage`accent-primary`; disabled falls back to `border-strong`.
ResponsiveFull 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

V2

Colour value selection with a visible hex read-back.

ColorInput

Live component

#006DEA

Used on the resident portal header.

Legacy mapping

Legacy branding screens used a bare native swatch with no value text.

ColorInput — standardized specification
AttributeSpecification
Defaultsize="medium"
Variantsswatch + value
Sizessmall (32px) · medium (40px) · large (48px)
Statesdefault · focus-visible · disabled · error
Icon behaviourNone; the swatch is the affordance.
Spacinggap-3 between swatch and value.
TypographyValue in `--ds-font-mono`, `text-body-sm`, uppercase.
Border radius`rounded-md` outer, `rounded-sm` swatch.
Colour usageChrome is neutral; the only saturated colour is the user's value.
ResponsiveSwatch 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

V2

Multi-line free text with optional character counter.

Textarea

Live component

0 / 280 characters

Legacy mapping

Legacy notes fields had no counter and inconsistent min-heights.

Textarea — standardized specification
AttributeSpecification
Default3 rows, resize-y
Variantsplain · with counter · auto-grow
Sizesrows-driven
Statesdefault · focus-visible · disabled · readonly · error · over limit
Icon behaviourNo inline icons; error messaging carries the AlertCircle glyph.
SpacingPadding 12px, field gap 6px.
Typography`text-body-md`; counter `text-caption`.
Border radius`rounded-md`.
Colour usageShared with Input; the counter turns `text-status-error` past the limit.
ResponsiveFull 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

V2

Single choice from a known list.

SelectSelectRootSelectTriggerSelectContentSelectItem

Live component

Legacy mapping

Legacy screens mixed native selects and custom dropdowns with different heights and no keyboard typeahead.

Select — standardized specification
AttributeSpecification
Defaultsize="medium", placeholder shown
Variantsoptions list · grouped options
Sizessmall · medium · large
Statesplaceholder · selected · open · focus-visible · disabled · error
Icon behaviourChevronDown at `--size-icon-sm`; Check marks the selected item.
SpacingTrigger padding 12px; item padding 6px/8px.
TypographyTrigger `text-body-sm|md`; group labels `text-label-sm`.
Border radius`rounded-md` trigger, `rounded-sm` items.
Colour usageMenu surface `bg-surface` with `shadow-lg`; highlighted item `bg-surface-hover`.
ResponsiveMenu 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

V2

Searchable selection: Combobox for one value, MultiSelect for many.

MultiSelectCombobox

Live component

Dementia care

Legacy mapping

Legacy tag/assignee pickers were bespoke per module. V2 provides one searchable pattern.

MultiSelect / Combobox — standardized specification
AttributeSpecification
Defaultsize="medium", search enabled
Variantssingle (Combobox) · multiple (MultiSelect)
Sizesmedium
Statesempty · searching · no results · selected · disabled · error
Icon behaviourChevronDown on the trigger, Check on selected rows, X on removable pills.
SpacingPill gap 6px; list item padding 6px/8px.
TypographyPills `text-label-sm`; options `text-body-sm`.
Border radius`rounded-md` trigger, `rounded-full` pills.
Colour usagePills on `bg-background-subtle`; the selected mark uses `text-text-link`.
ResponsivePills 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

V2

Calendar- and clock-assisted date, range and time selection.

DatePickerDateRangePickerTimePicker

Live 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.

DatePicker / TimePicker — standardized specification
AttributeSpecification
Defaultsize="medium", visible label
Variantssingle date · date range · time
Sizessmall · medium · large
Statesempty · selected · open · disabled · error
Icon behaviourCalendar / Clock leading icon at `--size-icon-sm`; navigation uses labelled IconButtons.
SpacingCalendar cell grid on the 4px scale; popover padding 12px.
TypographyTrigger `text-body-sm`; calendar cells `text-body-sm`.
Border radius`rounded-md` trigger, `rounded-sm` cells.
Colour usageSelected day `bg-primary`; range fill `bg-primary-subtle`.
ResponsivePopover 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.