Filter bar

Narrows a long list.

Part

  • bardefault

  • bar, applied

Menu

  • multiple, countsdefault

Chip

  • select

  • toggle

  • add

  • Method: POST

    add, set

  • Status Failed

    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
FilterBar
div props
FilterBarSearch value, onValueChange, shortcut
string, (v) => void, string | null default , , '/'
FilterBarSelect label, options
string, {value, label, count?, disabled?}[]
FilterBarSelect multiple, value, onValueChange
boolean, string | string[] default false
FilterBarSelect showValue, showAllOption, sinkEmpty
boolean default true, false, with counts
FilterBarAdd label, options, value
string, FilterOption[], string | null
FilterToggle
Chip props
FilterBarChips, FilterChip label, value, onRemove
div; string, ReactNode, () => void
FilterBarCount shown, total, noun
number, number, string
useFilteredRows
(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)

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.