Filter chip
Interactive filter dropdown. Selected and open states sit on the chip trigger.
Filter chip is a filter for a toolbar: a chip that opens a
Dropdown menu of options and shows the current choice on
its face. Its button is Chip trigger, at the lg size by
default, with the brand ring and the caret always out, and selected fills it while a filter is
applied, so a row of filters shows which ones are on. Chip is the
label that opens nothing.
Usage
import {
FilterChip,
FilterChipContent,
FilterChipRadioGroup,
FilterChipRadioItem,
FilterChipTrigger,
} from '@/components/ui/filter-chip';
<FilterChip>
<FilterChipTrigger>{labels[range]}</FilterChipTrigger>
<FilterChipContent>
<FilterChipRadioGroup value={range} onValueChange={setRange}>
<FilterChipRadioItem value="7d">Last 7 days</FilterChipRadioItem>
<FilterChipRadioItem value="30d">Last 30 days</FilterChipRadioItem>
</FilterChipRadioGroup>
</FilterChipContent>
</FilterChip>FilterChip is the Dropdown menu root, so it takes open, onOpenChange, and modal as that
does. You hold the value, and the chip's label follows it. The rows are Dropdown menu's rows under
new names: FilterChipLabel, FilterChipGroup, FilterChipItem, FilterChipSeparator,
FilterChipCheckboxItem, FilterChipRadioGroup, and FilterChipRadioItem. There are no submenu
or shortcut parts.
One choice or several
Radio rows pick one value, like a date range, and close the menu when picked. Checkbox rows turn
facets on and off, and the menu stays open while you toggle them, so setting several facets is one
visit. Pass closeOnSelect to a checkbox row that should close the menu.
Show that a filter is on
Pass selected while the filter is narrowing the results: for radio rows when the value is not the
default, and for checkbox rows when any facet is on. The chip fills with bg-tone-fill, the
same quiet tone fill as a Chip. Put the value in the label too, like "Status: Open", because the
fill is the only other sign.
Groups take a FilterChipLabel heading, a FilterChipSeparator sets them apart, and a plain
FilterChipItem runs an action, like a row that resets the filter.
The ring and the wash follow the look
The chip takes the primary tone, like Chip's outline, rather than Button's outline. The ring is
border-tone/(--alpha-32), hover and open wash it with bg-tone-fill-hover, and the label stays
text-fg in every state, at regular weight. Its corners stay at 8px in every look, so a look that
rounds buttons into pills, like ICS, does not reshape it, and it does not grow or move on hover. The
caret is a filled Phosphor CaretDown.
The menu opens 4px below the chip, lined up with its leading edge, and is at least 192px wide.
In a tab row
size="sm" sets the chip at 28px with text-xs and 16px icons, Chip trigger's sm metrics, with
the same ring, fill, and caret. At that height it sits in a row of header tabs without its bottom
edge landing on the header's rule, so a board or a roadmap can keep its filters beside its tabs.
lg stays the size for a toolbar of filters.
<FilterChip>
<FilterChipTrigger size="sm" selected={status !== 'all'}>
{statusLabel}
</FilterChipTrigger>
<FilterChipContent>{/* rows */}</FilterChipContent>
</FilterChip>Props
The root, the content, and the rows take the props of their Dropdown menu parts, plus these.
| Prop | Type | Default | What it does |
|---|---|---|---|
size | "lg" | "sm" | "lg" | On FilterChipTrigger. 40px for a toolbar, or 28px to sit in a tab row. |
selected | boolean | false | On FilterChipTrigger. Fills the chip with bg-tone-fill to show the filter is on. |
iconBefore | ComponentType<{ className?: string }> | On FilterChipTrigger. A 20px icon before the label. Ignored when mark is set. | |
mark | ReactNode | On FilterChipTrigger. Any node before the label, like a dot. Wins over iconBefore. | |
align | "start" | "center" | "end" | "start" | On FilterChipContent. Lines the menu up with the chip's leading edge. |
sideOffset | number | 4 | On FilterChipContent. The gap between the chip and the menu, in pixels. |
FilterChipTrigger takes Chip trigger's props without variant and caret, which are fixed at
outline and always. To change them, put a Chip trigger under a Dropdown menu yourself.
Accessibility
- The chip is a native
<button>witharia-haspopup="menu"andaria-expanded, and the open menu takes the chip as its label, so the menu is named by the chip's text. Enter, Space, or ↓ opens it and focuses the first row. - In the menu the keys are Dropdown menu's: ↑ and ↓ move between rows and stop at the ends, Home and End jump to the first and last, typing a letter moves to the next row that starts with it, and Enter or Space picks. Escape closes the menu and puts focus back on the chip.
- Radio rows are
menuitemradioand checkbox rows aremenuitemcheckbox, each witharia-checked, so the check on the right is read out as well. selectedis a fill and sets no ARIA state, and forced colors repaints the fill, so the chip's text is what tells a screen reader, or someone in Windows high contrast, that the filter is on. A chip that reads "Status: Open" says it, and one that reads "Status" does not.FilterChipLabelis plain text that the arrow keys skip, and it is not tied to its group, so give theFilterChipGroupanaria-labelwhen the heading matters.- Focus shows a 2px ring in the contrast ink on
:focus-visibleonly, and in forced colors a transparent outline that the system paints. - The chip is 40px tall at
lgand 28px atsm, and at least 44px tall on a touch screen at either size, and it does not grow wider. The rows are 32px and do not grow. modalis on by default, so while the menu is open the page behind it ignores the pointer and is hidden from assistive tech.- Under reduced motion the chip changes color without a transition, and the menu opens and closes without its fade.
When to use something else
- Chip trigger when the chip opens a picker other than a
filter menu, like a combobox, a popover, or a calendar, or for a band of properties that share one
quiet look, with the
ghostvariant, thesmsize, and a caret that waits for hover. - Chip for a value that opens nothing, like a picked option or an applied filter, with an optional dismiss button.
- Badge for a read-only status or count, like the number of
results, and for a value shown inside a button, because it renders a
<span>that nests there. - Category colors for a value that keeps its own
hue in every look, through
CategoryTag. - Toggle group when there are only a few options and they should all stay in view, one press each, with no menu to open.
- Combobox with
multipleto pick several values from a list long enough to search. This menu grows with its rows and does not scroll.
Install
shadcn add @gesso/filter-chipIt installs Chip trigger and Dropdown menu with it.