Filter bar
Narrows a long list.
Part
bardefault
bar, applied
Menu
multiple, countsdefault
Chip
select
toggle
add
add, set
applied
States
rest
hover
focus
set
several set
disabled
Usage
When to use it
- Use
- Above a list or a table of twenty or more rows that people narrow by text and by known fields.
- Use
- Filters that combine: a status and an agent and a model at once.
- Not for
- Switching between two to five fixed views of the same data. Use Segmented control.
- Not for
- Searching the whole product and running commands. Use Command.
- Not for
- A value a form submits. Use Select.
Rules
9 rules
- Must
- Pass each option's count from useFilteredRows, counted against the search, the other filters and the pressed toggles. Because a count against the whole list promises rows the other filters already hide, so the pick empties the list.
- Must
- Put a FilterBarCount in every bar. Because its status region is the only thing a screen reader hears when the list changes.
- Must
- Give FilterBar an aria-label naming what it filters. Because the bar is a search landmark, and unnamed ones read alike in the landmark list.
- Must
- Render FilterBarClear only while a filter or the search is set. Because a Clear all with nothing to clear is a dead tab stop that suggests a filter is still on.
- Must
- Keep shortcut="/" on the page's main bar and pass shortcut={null} to every other bar. Because only the first claiming search takes "/", so a second bar shows a hint for a key that focuses another field.
- Never
- Make a filter chip a primary Button or give it glass to show it is set. Because a row of filters has no lead; a set filter takes the pressed wash, and glass would make it read as the page's main action.
- Never
- Add an Apply button; apply each change as it happens. Because until the press the chips say one thing and the list another, and the count announces nothing.
- Should
- Pass showValue={false} to a FilterBarSelect whose values also show as FilterChips. Because otherwise the same filter reads twice, on the chip in the bar and on the chip below it.
- Should
- Name a filter with the column's one-word name; the chip adds the value.
State order
6 states, highest first
- 1
- disabled
- 2
- open
- 3
- set
- 4
- focus
- 5
- hover
- 6
- rest
Accessibility
5 checks
- The bar is a search landmark named by aria-label.
- A filter chip is a menu button named by the filter and its value ("Status: Failed, Waiting").
- The count speaks once typing pauses for 500ms, not on every key.
- Removing a chip moves focus to the next chip, or back to the filter that set it; Clear all hands focus to the search.
- A sunk option says why under its label, so its quiet text is not the only cue.
Keyboard
4 keys
- /
- Focuses the bar's search from outside a field, dialog or menu.
- Escape
- In the search, clears it; in a menu, closes it.
- Enter, Space, Down
- On a filter chip: opens its menu.
- Backspace, Delete
- On an applied chip's remove button: removes it.
Edge cases
5 edge cases
- Narrow row
- Under 28rem of bar the search takes its own row; the chips, the count and Clear all go under it, the count still at the end. Under 18rem the count is only announced.
- Option that leaves nothing
- Sinks under a rule, quiet, with "None left with the other filters" above it.
- Several values
- The chip shows the filter name and a count; the menu stays open between picks.
- Nothing left
- The count reads 0 of 240; show an EmptyState in the list with Clear all.
- Right to left
- The row runs right to left; the count keeps its own number order.
API
10 props
FilterBardiv propsFilterBarSearch value, onValueChange, shortcutstring, (v) => void, string | nulldefault, , '/'FilterBarSelect label, optionsstring, {value, label, count?, disabled?}[]FilterBarSelect multiple, value, onValueChangeboolean, string | string[]defaultfalseFilterBarSelect showValue, showAllOption, sinkEmptybooleandefaulttrue, false, with countsFilterBarAdd label, options, valuestring, FilterOption[], string | nullFilterToggleChip propsFilterBarChips, FilterChip label, value, onRemovediv; string, ReactNode, () => voidFilterBarCount shown, total, nounnumber, number, stringuseFilteredRows(rows, {search, match, fields, filters, where})
Tokens
16 tokens
--paper- #ffffff
--line-control- rgb(5 5 5 / 0.15)
--line-focus- rgb(5 5 5 / 0.32)
--line-faint- rgb(5 5 5 / 0.06)
--pressed- rgb(5 5 5 / 0.10)
--hover- rgb(5 5 5 / 0.06)
--text-1- rgb(5 5 5 / 0.80)
--text-2- rgb(5 5 5 / 0.60)
--text-3- rgb(5 5 5 / 0.45)
--text-disabled- rgb(5 5 5 / 0.32)
--control-sm- 26px
--menu-row-sm- 28px
--popover- 288px
--radius-pill- 999px
--type-label-sm- 500 12px/1.35 var(--font-sans)
--type-caption- 400 12px/1.35 var(--font-sans)