Skip to main content

Selector

Allows users to choose from a set of options presented as selectable cards or tiles. Use when options benefit from richer visual presentation than a radio button or dropdown provides. Don't use when there are more than 6–8 options.

OverviewStyleAccessibility

Description

Anatomy

PartRequired?Notes
Tile container Required The outer interactive element. Renders as a div with role="radio" or role="checkbox" depending on selection mode. Carries all border, background, and focus ring styles.
Icon or visual mark Optional A wa-icon or illustrative mark placed at the top or left of the tile to aid quick scanning. Should reinforce, not replace, the label.
Label Required Primary text identifying the option. Should be concise — one to four words is ideal. Uses --op-text-sm, semibold weight, --op-color-text-primary.
Supporting description Optional Secondary text beneath the label providing additional context. Use when the label alone is ambiguous. Uses --op-text-xs, regular weight, --op-color-text-secondary.
Selected indicator Required Visual treatment — border colour change, background fill, and a checkmark or radio icon — that communicates selected state. Must not rely on colour alone.
Group label Recommended A visible fieldset legend or heading labelling the entire selector group. Required for accessibility; recommended to always be visible in the UI.

Variants

States

State Behaviour
Default (unselected) Tile displays with --op-color-bg-primary background and 1px --op-color-border-default border. Label and description use standard text colours.
Hover Background shifts to --op-color-bg-secondary; border colour shifts to --op-color-interactive-hover. Cursor is pointer.
Focus 3px focus ring in --op-color-interactive-focus appears outside the tile border at 2px offset. Focus is visible on keyboard navigation and must not be suppressed.
Selected Border becomes 2px solid --op-color-interactive-default; background may carry a subtle tint. A checkmark icon (single-select: circle-check, multi-select: square-check) appears in --op-color-interactive-default.
Selected and focused Selected visual treatment is maintained and the 3px focus ring is applied simultaneously. Both indicators are visible at the same time.
Disabled Tile is non-interactive. Whole tile has opacity: 0.4. Cursor is not-allowed. aria-disabled='true' is set; tile is removed from tab order (tabindex='-1').
Error (group-level) A group-level error message appears below the tile set using --op-color-status-error with an accompanying icon. Individual tiles are not individually marked invalid; the fieldset group conveys the error via aria-describedby.

Usage guidelines

Do / Don't

Do

Keep tile labels to four words or fewer so users can scan quickly.

Don't

Don't write paragraph-length labels inside a tile — move long explanations to hint text outside the tile.

Do

Always include a visible group label (fieldset legend) so the purpose of the selector is clear to all users.

Don't

Don't rely on surrounding page context alone to communicate what the selector group is asking.

Do

Pre-select a sensible default when one option is clearly most common, and explain the default in hint text.

Don't

Don't leave all tiles unselected with no guidance when a default would reduce cognitive load.

Do

Display tiles in a consistent grid or row so users can compare options spatially.

Don't

Don't vary tile sizes within a group — inconsistent sizing implies hierarchy that does not exist.

Do

Show a group-level error message with actionable text when validation fails (e.g. 'Select a consultation type to continue').

Don't

Don't use a red border on individual tiles as the sole error indicator — this fails WCAG 1.4.1 (use of colour).

Layout & Spacing

Tile group: display grid; gap --op-space-12 (12px) between tiles. Default grid: repeat(auto-fill, minmax(180px, 1fr)); max 4 columns on desktop. Tile padding: --op-space-16 (16px) on all sides. Icon size: 24px, margin-bottom --op-space-8 (8px) when stacked above label. Label margin-bottom (when description follows): --op-space-4 (4px). Group label margin-bottom: --op-space-12 (12px). Error message margin-top: --op-space-8 (8px). Minimum tile height: 80px (ensures at least 44px touch target with padding).

Tokens

PartTokenValue
Tile container — default background --op-color-bg-primary White in light mode; dark surface in dark mode.
Tile container — default border --op-color-border-default 1px solid. Transitions to interactive-default at 2px on selection.
Tile container — hover background --op-color-bg-secondary Subtle tint to signal interactivity on hover.
Tile container — hover border --op-color-interactive-hover Applied on hover before selection.
Tile container — selected border --op-color-interactive-default 2px solid (green-400). Increased weight reinforces selection.
Tile container — focus ring --op-color-interactive-focus 3px outline, 2px offset. Always visible on keyboard focus.
Tile container — border radius --op-radius-md 8px. Consistent with card and input components.
Label text --op-color-text-primary Semibold weight, --op-text-sm.
Description text --op-color-text-secondary Regular weight, --op-text-xs.
Icon (unselected) --op-color-text-secondary Shifts to --op-color-interactive-default when tile is selected.
Selected indicator icon --op-color-interactive-default Rendered as wa-icon; do not use background colour as sole indicator.
Error message text --op-color-status-error Group-level only. Always accompanied by an error icon for non-colour indication.
Disabled opacity opacity: 0.4 Applied to the whole tile. Acceptable under WCAG 1.4.3 for inactive components.

Engineering notes

  • No wa-* web component exists for Selector. Implement using a fieldset/legend wrapper with styled div elements carrying appropriate ARIA roles.
  • For single-select, apply role='radiogroup' on the fieldset and role='radio' with aria-checked on each tile. Use the roving tabindex pattern: selected (or first) tile has tabindex='0'; all others have tabindex='-1'. Arrow keys update focus and selection.
  • For multi-select, apply role='group' on the fieldset and role='checkbox' with aria-checked on each tile. Every tile retains tabindex='0' so users can Tab through all options.
  • Manage aria-checked updates in JavaScript on click and keyboard events. Do not use native radio/checkbox inputs for visual tiles — use hidden inputs if native form submission is required.
  • Apply aria-disabled='true' and tabindex='-1' on disabled tiles. Do not use the HTML disabled attribute on non-input elements.
  • For group-level validation, set aria-describedby on the fieldset pointing to the error message element. Add role='alert' to the error element so screen readers announce it on appearance.
  • Respect prefers-reduced-motion: remove background and border transition animations when this media query is active.
  • Ensure tiles meet 44x44px minimum touch target. With --op-space-16 padding and 80px min-height this is met. Verify icon-only tiles explicitly.

Keyboard interaction

KeyAction
Tab Moves focus into the selector group (to the selected tile, or the first tile if none is selected). Tab again moves focus out of the group entirely.
Arrow Right / Arrow Down Single-select only: moves focus and selection to the next tile in the group (roving tabindex).
Arrow Left / Arrow Up Single-select only: moves focus and selection to the previous tile in the group.
Space Multi-select: toggles the focused tile. Single-select: selects the focused tile.
Enter Activates the focused tile. Equivalent to Space for selection purposes.
Escape No default action within the selector. If inside a dialog, Escape closes the dialog per that component's behaviour.

Why it matters

Government platforms serve users with a wide range of abilities, including those relying on keyboard-only navigation or screen readers. Selectors that look interactive but do not support keyboard access exclude a legally protected group of users and may breach the Disability Discrimination Act (Australia) or equivalent legislation in other jurisdictions. Correct ARIA roles and keyboard patterns are mandatory.

Focus

Focus ring: 3px solid --op-color-interactive-focus, 2px offset, applied to the tile container. Focus must always be visible — do not suppress outline with outline: none. Use :focus-visible only with a polyfill for older browsers. Single-select groups use a roving tabindex: the selected (or first) tile has tabindex="0"; all others have tabindex="-1". Arrow key navigation updates which tile holds tabindex="0". Multi-select groups give every tile tabindex="0" so users can Tab through and toggle each option independently.

ARIA

Role or attributeWhen to useExample
role="radiogroup" Applied to the fieldset or wrapper of a single-select selector group. <fieldset role="radiogroup" aria-labelledby="selector-group-label">
role="radio" Applied to each tile in a single-select group. <div role="radio" aria-checked="false" tabindex="-1">
role="group" Applied to the wrapper of a multi-select selector group. <fieldset role="group" aria-labelledby="channels-label">
role="checkbox" Applied to each tile in a multi-select group. <div role="checkbox" aria-checked="false" tabindex="0">
aria-checked Reflects selection state. Must be updated via JavaScript on interaction. Values: 'true' or 'false'. aria-checked="true"
aria-labelledby Points the group wrapper to the visible group label (legend or heading ID). aria-labelledby="consult-type-label"
aria-describedby Points the group wrapper to an error message or hint text element. aria-describedby="consult-type-error"
aria-disabled Set to 'true' on individual tiles that are disabled. Do not use the HTML disabled attribute on non-native elements. aria-disabled="true"

Contrast

Label text (--op-color-text-primary) on tile background (--op-color-bg-primary): must meet 4.5:1 for --op-text-sm normal weight. Description text (--op-color-text-secondary) on --op-color-bg-primary: must meet 4.5:1; verify in both light and dark modes. Selected border (--op-color-interactive-default / green-400) does not need to meet text contrast ratios, but selected state must be communicated by means other than colour alone. Error message text (--op-color-status-error) on --op-color-bg-primary: must meet 4.5:1. Disabled tiles at 40% opacity will not meet contrast thresholds — this is acceptable under WCAG 1.4.3 exception for inactive UI components, provided aria-disabled is set correctly.

Touch targets

Each tile must have a minimum touch target of 44x44px. With --op-space-16 (16px) padding on all sides and a minimum tile height of 80px, this is met for any tile with a single-line label. Verify icon-only tile variants explicitly. On mobile, consider a full-width stacked layout to further increase tap area.

Things to avoid

  • Using colour as the only visual indicator of selected state.
  • Suppressing the focus ring with outline: none or outline: 0 without a robust :focus-visible replacement.
  • Forgetting to update aria-checked in JavaScript when a tile is selected or deselected.
  • Placing a selector group inside another interactive element without verifying Tab and Escape key behaviour is correct.
  • Using non-descriptive group labels such as 'Options' — always describe what is being selected.
  • Setting different tile widths or heights within the same group, implying a hierarchy that does not exist.

Was this page helpful?

Updated 2 October 2026