What's new Report an issue
GIG Cymru NHS Wales, Digital Health and Care Wales

Search

Find content by typing. The only search field in the system.

Basic

A live inline filter: results update as the user types, with no submit step. Use it where the results are cheap to recompute and visible without scrolling.

<div class="sr-search">
  <label class="sr-visually-hidden" for="q">Search patients</label>
  <div class="sr-search__row">
    <div class="sr-search__control">
      <div class="sr-search__field">
        <span class="sr-search__icon"><!-- nav/search --></span>
        <input type="search" class="sr-search__control-input" id="q"
               placeholder="Search patients" autocomplete="off">
        <button type="button" class="sr-search__clear" aria-label="Clear search">
          <!-- nav/clear -->
        </button>
      </div>
    </div>
    <button type="submit" class="sr-search__submit" disabled>Search</button>
  </div>
</div>

<!-- Typeahead adds role="combobox" + a role="listbox" popover. The arrow keys,
     Home/End, Enter and Esc are yours to wire up. -->

With a button

For a query that costs something — a backend call, a cross-organisation lookup. The button is disabled until there is a query, so an empty search cannot be sent. Type into the field and watch it enable.

<div class="sr-search">
  <label class="sr-visually-hidden" for="q">Search patients</label>
  <div class="sr-search__row">
    <div class="sr-search__control">
      <div class="sr-search__field">
        <span class="sr-search__icon"><!-- nav/search --></span>
        <input type="search" class="sr-search__control-input" id="q"
               placeholder="Search patients" autocomplete="off">
        <button type="button" class="sr-search__clear" aria-label="Clear search">
          <!-- nav/clear -->
        </button>
      </div>
    </div>
    <button type="submit" class="sr-search__submit" disabled>Search</button>
  </div>
</div>

<!-- Typeahead adds role="combobox" + a role="listbox" popover. The arrow keys,
     Home/End, Enter and Esc are yours to wire up. -->

With an icon button

The same control where a text button will not fit — mobile, dense toolbars. The button is 40×40, square to the field and comfortably over the 24×24 minimum in WCAG 2.2. Use it because space demands it, not by preference: the word "Search" is easier to read at a glance than a glyph.

<div class="sr-search">
  <label class="sr-visually-hidden" for="q">Search patients</label>
  <div class="sr-search__row">
    <div class="sr-search__control">
      <div class="sr-search__field">
        <span class="sr-search__icon"><!-- nav/search --></span>
        <input type="search" class="sr-search__control-input" id="q"
               placeholder="Search patients" autocomplete="off">
        <button type="button" class="sr-search__clear" aria-label="Clear search">
          <!-- nav/clear -->
        </button>
      </div>
    </div>
    <button type="submit" class="sr-search__submit" disabled>Search</button>
  </div>
</div>

<!-- Typeahead adds role="combobox" + a role="listbox" popover. The arrow keys,
     Home/End, Enter and Esc are yours to wire up. -->

Typeahead

Type to look something up, then pick a result. Focus the field below and use Home End Enter Esc — the whole model works without the mouse. The matched run is bold, and the second line disambiguates results that would otherwise read alike.

<div class="sr-search">
  <label class="sr-visually-hidden" for="q">Search patients</label>
  <div class="sr-search__row">
    <div class="sr-search__control">
      <div class="sr-search__field">
        <span class="sr-search__icon"><!-- nav/search --></span>
        <input type="search" class="sr-search__control-input" id="q"
               placeholder="Search patients" autocomplete="off">
        <button type="button" class="sr-search__clear" aria-label="Clear search">
          <!-- nav/clear -->
        </button>
      </div>
    </div>
    <button type="submit" class="sr-search__submit" disabled>Search</button>
  </div>
</div>

<!-- Typeahead adds role="combobox" + a role="listbox" popover. The arrow keys,
     Home/End, Enter and Esc are yours to wire up. -->

No results

The failed query is quoted back, so the user can see what was actually tried. It is usually a typo they spot instantly.

<div class="sr-search">
  <label class="sr-visually-hidden" for="q">Search patients</label>
  <div class="sr-search__row">
    <div class="sr-search__control">
      <div class="sr-search__field">
        <span class="sr-search__icon"><!-- nav/search --></span>
        <input type="search" class="sr-search__control-input" id="q"
               placeholder="Search patients" autocomplete="off">
        <button type="button" class="sr-search__clear" aria-label="Clear search">
          <!-- nav/clear -->
        </button>
      </div>
    </div>
    <button type="submit" class="sr-search__submit" disabled>Search</button>
  </div>
</div>

<!-- Typeahead adds role="combobox" + a role="listbox" popover. The arrow keys,
     Home/End, Enter and Esc are yours to wire up. -->

States

Filled shows the clear affordance; loading replaces it with a spinner and announces "Searching…" to assistive technology; error carries an icon and a message, never the red border alone.

As a labelled form field

Search carries its own Label, Hint and Required properties. Do not wrap an Input around it — the Input set's Type=Search variants were removed on 4 June 2026, and Search is now the only search field in the system.


Source for both the Figma "Guidelines / Usage notes" panel and the design system website page. Do not fork this content between the two.

Figma: Search (1715:375), Search Suggestions (1716:238) on page 1701:17851 Code: packages/web/src/search/search.css, @dhcw/sr-reactSearch


When to use

Use Search when the user finds content by typing, and the result is a list or view that changes because of what they typed.

You wantUse
Filter a table, list or panel as the user typesSearch, Type=Basic
A query sent to a service, patient lookup, document searchSearch, Type=With Button
The same, where horizontal space is tight (mobile, toolbars)Search, Type=With Icon Button
Type to look up a person, place or coded term, then pick a resultSearch, Type=Typeahead
Pick one value from a known set, typing to narrow itAutocomplete
Pick one value from a short fixed setSelect
Free text that is not a queryInput
Perform an actionButton

Search is the only search field in the system. The Input set's Type=Search variants were removed on 2026-06-04. To put a search inside a labelled form field, turn on Label / Hint / Required on a Search instance, do not wrap an Input around it.


Choosing a type


Content


Accessibility


Engineering


Do / Don't


Known gaps

Accessibility requirements

RequirementWCAG SCHow Single Record meets itTest method
The field has a name1.3.1, 4.1.2Every field has a real label. A standalone search bar hides it with .sr-visually-hidden, which removes the visual footprint and nothing else.Screen reader announce
Suggestions follow the combobox pattern4.1.2role="combobox" with aria-expanded, aria-controls and aria-activedescendant; the popover is role="listbox" with role="option" rows.Screen reader, arrow through the list
The whole list is reachable by keyboard2.1.1↓ ↑ move the active row, Home/End jump to the ends, Enter selects, Esc closes. Esc does not clear the query.Keyboard only
Focus is visible and does not move the layout1.4.11, 2.4.7A 3px Border/Focus ring outside the 1px border, so nothing in the row shifts when focus lands.Keyboard tab
The matched run is not colour alone1.4.1The match is bolded, and the row carries aria-selected and aria-activedescendant regardless of how it looks.Greyscale review
Loading is announced4.1.3A role="status" live region says "Searching…" alongside the spinner, so the wait is not visual only.Screen reader, slow network
Errors are exposed and actionable3.3.1, 3.3.3aria-invalid on the input, the message linked by aria-describedby, and an icon beside it so the error is not the border colour.Screen reader, submit short query
Controls meet the target size2.5.8The icon button is 40×40 and the field is 40px tall. The clear button is 24×24, the minimum, and sits inside a 40px row.Measure
Clear does not strand focus2.4.3Clearing empties the field and returns focus to it, rather than leaving focus on a button that has just disappeared.Keyboard only
Text resizes to 200%1.4.4The field and popover size from their content; suggestion rows wrap onto two lines rather than truncating.Browser zoom to 200%