Search
A text input that lets users search or filter content, with live suggestions as they type. Use for any search or filter interaction across the product. Don't use a standard Input field for search interactions.
Description
What it does
Provides a dedicated search input that accepts typed queries and optionally surfaces live suggestions or filtered results as the user types. Submitting the query triggers a search action.
Where it appears
Global navigation bars, data table toolbars, stakeholder directory pages, consultation lists, and any screen where users need to locate or filter records.
Why it exists
Search is a distinct interaction pattern from general form input. A purpose-built component ensures consistent keyboard behaviour, correct ARIA semantics (role='search', role='combobox'), and a recognisable affordance — the magnifying-glass icon — that signals searchability to users at a glance.
Anatomy
| Part | Required? | Notes |
|---|---|---|
| Search container | Required | Wraps the entire control. Carries role='search' (or is nested inside a <form role='search'>) to expose the landmark to assistive technology. |
| Search icon | Recommended | Magnifying-glass icon rendered at the leading edge of the input. Provides a visual affordance. Hidden from the accessibility tree with aria-hidden='true' — the input's label conveys purpose. |
| Text input | Required | The editable field. Receives focus, accepts keyboard input, and fires input/change events that drive filtering or suggestion logic. |
| Clear button | Optional | Appears when the field has a value. Allows one-click dismissal of the current query. Must have an accessible label — 'Clear search' — and return focus to the text input after activation. |
| Suggestions dropdown | Optional | A listbox that appears beneath the input when live suggestions are enabled. Each option is a focusable listbox item. Dismissed on Escape or when focus leaves the composite. |
| Label | Required | Visually hidden labels are acceptable for compact nav-bar placements, but a visible label is required on standalone search panels. Always present in the DOM for screen readers. |
Variants
Default
Full-width search input with visible label, search icon, and optional clear button. No suggestions.
Standalone search panels, table toolbar filters, and anywhere screen space allows a labelled field.
Compact
Reduced-height input with visually hidden label, suitable for navigation bars and dense toolbars.
Global nav search, sidebar filters, and narrow column layouts where vertical space is at a premium.
With suggestions
Adds a suggestions dropdown (combobox pattern) that renders matching options as the user types.
Stakeholder directory lookups, consultation search where known records can be surfaced instantly, and anywhere type-ahead reduces cognitive effort.
With filters
Search input paired with one or more filter chips or a filter trigger button to narrow result scope.
Complex list views with multiple filterable dimensions — for example, filtering consultations by status and region simultaneously.
States
| State | Behaviour |
|---|---|
| Default (empty) | Placeholder text is visible. Search icon is shown. Clear button is hidden. Suggestions dropdown is closed. |
| Focus | 3px focus ring using --op-color-interactive-focus appears around the input container. Suggestions dropdown may open if the field already has a value. |
| Active (has value) | Placeholder is replaced by user input. Clear button becomes visible. If suggestions variant, the dropdown opens and filters to matching options. |
| Loading | A spinner replaces or supplements the search icon to indicate an async lookup is in progress. The input remains editable. Announce 'Loading results' to screen readers via a live region. |
| No results | Suggestions dropdown shows a single non-interactive message item — 'No results found' — when the query matches nothing. Does not close the dropdown automatically. |
| Disabled | Input is non-interactive. Visually dimmed using --op-color-text-disabled and --op-color-bg-disabled. aria-disabled='true' is set. Do not use disabled for loading states — use the Loading state instead. |
| Error | Border switches to --op-color-status-error. An error message appears below the input. Rare for search — typically used when a query format is invalid (e.g. special characters not permitted). |
Usage guidelines
When to use
- Users need to locate a specific record from a large dataset — for example, finding a stakeholder by name in the Open Point directory.
- A list or table can be filtered in real time as the user types.
- A consultation landing page requires a prominent entry point to search published engagements.
- Navigation requires a global search that spans multiple record types.
When not to use
- The dataset is small enough to scan visually — use a filter or select instead to avoid unnecessary complexity.
- The interaction is a form field collecting free-text input for submission — use a standard Input component.
- You need a multi-select filter — use Multi-Select.
- The search triggers a full page navigation — ensure the component is wrapped in a <form> element with method and action attributes so it degrades gracefully without JavaScript.
Do / Don't
Do
Always provide a visible or visually hidden label. Never rely on placeholder text alone as the label.
Don't
Do not use placeholder text as a substitute for a label — placeholder disappears on input and is not reliably announced by all screen readers.
Do
Use role='search' on the wrapping landmark so screen reader users can navigate to the search region directly.
Don't
Do not wrap the search input in a generic div without a landmark role — it becomes invisible to assistive technology navigation.
Do
Debounce live-search queries by 200–300ms to avoid firing a network request on every keystroke.
Don't
Do not fire search requests on every keydown — this creates excessive network load and degrades performance on low-bandwidth government networks.
Do
Announce result counts to screen readers using a live region: 'Showing 14 results for stakeholders'.
Don't
Do not update results silently — users relying on screen readers will have no indication that the list has changed.
Do
Return focus to the search input after the user clears the field with the Clear button.
Don't
Do not move focus to an unrelated element after clearing — it disorients keyboard and screen reader users.
Layout & Spacing
Input height: 24 / 32 / 40 / 48 / 56px (XS to XL), medium 40px, from --orbit-form-control-size-*-height. Add a 44px touch target around the smaller sizes.
Shape: rectangular by default, or pill (--orbit-form-control-radius-pill).
Horizontal padding inside input: 8 / 12 / 16 / 20 / 24px by size, --orbit-form-control-size-*-padding-inline
Search icon size: 20x20px; gap between icon and text: --op-space-8 (8px)
Clear button: 20x20px icon, right-aligned, margin-right --op-space-12 (12px)
Label margin-bottom: --op-space-4 (4px)
Suggestions dropdown: margin-top --op-space-4 (4px) from input bottom edge; border-radius --op-radius-md (8px); max-height 320px with overflow-y scroll
Each suggestion item: padding --op-space-8 (8px) --op-space-12 (12px); min-height 44px
Error message: margin-top --op-space-4 (4px); font-size --op-text-sm
Tokens
| Part | Token | Value |
|---|---|---|
| Input background | --op-color-bg-primary | White in light mode; dark surface in dark mode. |
| Input border (default) | --op-color-border-default | 1px solid border. |
| Input border (focus) | --op-color-interactive-focus | 3px focus ring, offset 2px. |
| Input text | --op-color-text-primary | Primary text colour for entered query. |
| Placeholder text | --op-color-text-placeholder | Subdued; must still meet 3:1 against input background. |
| Search icon | --op-color-text-secondary | Slightly subdued to avoid competing with input text. |
| Clear button icon | --op-color-text-secondary | Matches search icon; darkens to --op-color-text-primary on hover. |
| Suggestions dropdown background | --op-color-bg-primary | Matches input background for visual continuity. |
| Suggestions dropdown border | --op-color-border-default | 1px solid, same as input border. |
| Suggestion item (hover/focus) | --op-color-bg-secondary | Subtle highlight; do not use interactive green for hover backgrounds. |
| Suggestion item (selected/active) | --op-color-interactive-default | Green-400; white text on top. Used when an item is keyboard-activated. |
| Error border | --op-color-status-error | Replaces default border colour. |
| Error message text | --op-color-status-error | Paired with --op-color-status-error-bg for inline error banners if needed. |
| Disabled input | --op-color-bg-disabled / --op-color-text-disabled | Background and text both dimmed; pointer-events: none. |
Engineering notes
- The search container should be a <form role='search'> element (or a <div role='search'>) so screen reader users can navigate to it via landmark navigation.
- Use aria-label on the form/container when there is no visible heading nearby — for example, aria-label='Stakeholder search'.
- When suggestions are enabled, implement the ARIA combobox pattern: the input receives role='combobox', aria-expanded, aria-controls pointing to the listbox id, and aria-autocomplete='list'.
- Each suggestion item should have role='option' and a unique id. Set aria-activedescendant on the input to match the currently highlighted option id.
- Use a visually hidden aria-live='polite' region to announce result counts after debounce resolves — for example, 'Showing 8 results'.
- The Clear button must have aria-label='Clear search' (or equivalent localised string) and type='button' to avoid submitting the form.
- Apply @media (prefers-reduced-motion: reduce) to disable any suggestion dropdown animation transitions.
- Debounce the input handler at 250ms before triggering async lookups. Cancel in-flight requests if a newer query supersedes them.
- wa-input can be used as the base element; add the search icon via the prefix slot and the clear button via the suffix slot.
Keyboard interaction
| Key | Action |
|---|---|
| Tab | Moves focus into the search input from the preceding focusable element. If the suggestions dropdown is open, Tab closes it and moves focus to the next element outside the composite. |
| Shift+Tab | Moves focus to the preceding focusable element. Closes suggestions dropdown if open. |
| Enter | Submits the search query, or — if a suggestion is highlighted — selects that suggestion and populates the input. |
| Escape | Closes the suggestions dropdown without selecting an option. Focus remains on the input. |
| Arrow Down | Opens the suggestions dropdown (if closed and value is present) and moves highlight to the first option. |
| Arrow Up | Moves highlight to the previous suggestion option. Wraps from first option back to the input. |
| Arrow Down / Arrow Up (in list) | Moves highlight between suggestion options. Does not move DOM focus — aria-activedescendant tracks the highlighted option. |
| Home / End | In the input field, moves cursor to start/end of text. If highlight is in the suggestions list, moves highlight to first/last option. |
Why it matters
Government platforms serve users with a wide range of access needs, including screen reader users navigating by landmark and keyboard-only users who cannot use a mouse. Search is often a primary navigation mechanism — getting its semantics wrong (missing role='search', broken combobox pattern) can make entire record sets inaccessible.
Focus
Focus ring: 3px solid --op-color-interactive-focus, offset 2px, on the input element. The suggestions dropdown does not receive DOM focus — keyboard navigation through options is managed via aria-activedescendant on the input, keeping focus on the input at all times. After the Clear button is activated, focus is programmatically returned to the text input. When the suggestions dropdown closes (Escape or selection), focus remains on or returns to the text input.
ARIA
| Role or attribute | When to use | Example |
|---|---|---|
role='search' | Applied to the wrapping <form> or <div> to expose the search landmark. | <form role="search" aria-label="Stakeholder search"> |
role='combobox' | Applied to the <input> when suggestions are enabled. | <input type="search" role="combobox" aria-expanded="true" aria-controls="search-listbox" aria-autocomplete="list" /> |
aria-expanded | On the combobox input. true when the suggestions dropdown is open, false when closed. | aria-expanded="false" |
aria-controls | Points from the combobox input to the id of the suggestions listbox. | aria-controls="search-suggestions" |
aria-activedescendant | Set to the id of the currently highlighted suggestion option. Empty string when no option is highlighted. | aria-activedescendant="suggestion-3" |
role='listbox' | Applied to the suggestions dropdown container. | <ul role="listbox" id="search-suggestions"> |
role='option' | Applied to each item in the suggestions listbox. | <li role="option" id="suggestion-1" aria-selected="false">Jane Smith</li> |
aria-label (clear button) | Provides an accessible name for the icon-only Clear button. | <button type="button" aria-label="Clear search"><wa-icon name="x"></wa-icon></button> |
aria-live='polite' | On a visually hidden status region to announce result counts after search resolves. | <div aria-live="polite" class="sr-only">Showing 14 results</div> |
Contrast
Input text (--op-color-text-primary) against input background (--op-color-bg-primary): must meet 4.5:1 (WCAG AA for normal text). Placeholder text (--op-color-text-placeholder) against input background: minimum 3:1 (WCAG AA for UI components). Do not use placeholder as a label substitute even if contrast passes. Search icon (--op-color-text-secondary) against input background: minimum 3:1 as a UI component. Selected suggestion text (white) against --op-color-interactive-default (green-400): verify at implementation time — green-400 must achieve 4.5:1 against white. Swap to a darker green shade if it does not. Focus ring (--op-color-interactive-focus) against adjacent background: minimum 3:1 as a focus indicator.
Touch targets
The text input must be at least 44px tall in the default variant to meet the 44x44px minimum touch target. The Clear button icon must have a clickable/tappable area of at least 44x44px — use padding to extend the touch area without enlarging the visible icon. The compact variant (36px height) is restricted to mouse/keyboard desktop contexts only and must not appear on mobile viewports.
Things to avoid
- Do not rely on placeholder text as the accessible label — always provide a <label> or aria-label.
- Do not move DOM focus into the suggestions listbox — use aria-activedescendant on the input instead.
- Do not auto-submit the search form on every keystroke — this causes unexpected page changes and disrupts screen reader users mid-input.
- Do not suppress the focus ring in any state — some teams remove outlines for visual polish, which fails WCAG 2.4.7.
- Do not use role='search' on an input element — apply it to the wrapping container (form or div) only.
- Do not close the suggestions dropdown on blur without a delay — clicking a suggestion fires blur on the input before the click event; a short delay (150ms) prevents the list closing before the selection registers.