NS Design System
v3.0.0 · 257 tokens

24Combobox

Type-ahead over a known list — a course, a tag, an org. A <select> cannot be searched and a bare text input cannot be trusted; this is the third thing.

Use it for

  • A list too long to scan but closed enough to validate against
  • Where the reader knows roughly what they want and can type it faster than they can find it

Not for

  • Under about seven options — use a Select; searching five things is friction, not help
  • Free text — if any value is allowed it is an Input, not a combobox
  • Multi-select — use Tag input

Open, filtering

The matched run is emphasised inside each option so the reader can see WHY a row matched.

  • Apex fundamentals12 lessons
  • Apex triggers in depth8 lessons
  • Testing Apex6 lessons
markup
<div class="ns-combobox">
  <input class="ns-input" type="text" role="combobox" aria-expanded="true" aria-controls="cb-list" aria-autocomplete="list" value="apex" autocomplete="off">
  <ul class="ns-combobox__list" id="cb-list" role="listbox">
    <li class="ns-combobox__option" role="option" aria-selected="true"><span><span class="ns-combobox__match">Apex</span> fundamentals</span><span class="ns-combobox__meta">12 lessons</span></li>
    <li class="ns-combobox__option" role="option" aria-selected="false"><span><span class="ns-combobox__match">Apex</span> triggers in depth</span><span class="ns-combobox__meta">8 lessons</span></li>
    <li class="ns-combobox__option" role="option" aria-selected="false"><span>Testing <span class="ns-combobox__match">Apex</span></span><span class="ns-combobox__meta">6 lessons</span></li>
  </ul>
</div>

No matches

Say what was searched. “No results” alone leaves the reader unsure whether the query or the data is wrong.

  • No course matches vlocity.
markup
<div class="ns-combobox">
  <input class="ns-input" type="text" role="combobox" aria-expanded="true" aria-controls="cb-empty" value="vlocity" autocomplete="off">
  <ul class="ns-combobox__list" id="cb-empty" role="listbox">
    <li class="ns-combobox__empty" role="option" aria-selected="false">No course matches <strong>vlocity</strong>.</li>
  </ul>
</div>

Accessibility contract

  • A real <input>, so autofill, IME, mobile keyboards and paste all behave
  • aria-expanded on the input; role=listbox / role=option on the list
  • aria-activedescendant points at the highlighted option, so the highlight is never a class the assistive layer cannot see
  • Without JS the input still submits its value — a search box that types beats a combobox that does nothing