Search, filter and sort
Finding things in a list or table.
Search sits on the left of a list's toolbar and filters on the right. Search and filters run on the server, so they cover every row, not just the loaded page. Column headers sort and filter their own column.
The problem
Bridge's lists get long: an organization's People tab, or a profile's intros, can run to hundreds of rows. If search only looks at the rows already loaded, people decide someone isn't there when they are. And if a filter is on but looks off, or a count doesn't match the list, they stop trusting what they see.
What to do
- Search on the left, filters on the right. SearchInput starts the toolbar and a FilterPill ends it. A filter or sort that belongs to one column lives in that column's header.
- Cover every row. Search waits about 300ms after typing stops, then searches the whole list. Where the list comes in pages, search and filters run on the server, so a match is never missed because it wasn't loaded.
- Make the state visible. An active filter looks active, the count comes from the rows on screen ("3 of 8 intros"), and a list filtered to nothing says so and keeps the way back in reach.
Which one do I use?
| Situation | Do this | Bridge example |
|---|---|---|
| Find someone by name in a list or table | SearchInput on the left of the toolbar | Organization → People: "Search people…" |
| Narrow the whole table by one property with a few choices | FilterPill on the right of the toolbar; the first option is "All …" | All degrees · 1st degree · 2nd degree |
| Narrow by a value that belongs to one column | ColumnFilterMenu in that column's header | Intros table → What happened: All stages, Waiting, Forwarded… |
| Two or three views of one feed, always visible | A row of FilterChips, each with its count | Activity: All · To do · Updates |
| Order a table by a date or an amount | ColumnSortButton in that column's header | Intros table → Made, Last updated, Value |
| Nothing matches | Keep the toolbar and header; say nothing matched; offer "Clear column filters" if a column filter is on | No intros match this filter. |
See it work
Live examples built from the package on the sample network. Click through them.
Search on the left, a filter pill on the right
Type a name: it searches once you stop typing. Pick a degree. The count follows the rows. Type "zz" to see no results. The toolbar stays while the list loads or fails.
Filter and sort from the column headers
Pick a connector or a stage, or sort by Made (newest first, oldest first, then back). Choose Canceled to filter to nothing: the header stays, and Clear column filters brings everything back.
8 of 8 intros
What to write
| Part | Rule | Example |
|---|---|---|
| Search placeholder | "Search" plus what it searches. It's a hint, not the label. | Search people… |
| First filter option | "All" plus the thing, so the resting state says what it means. Never just "Filter". | All degrees, All stages, All connectors |
| Filter options | Short nouns, sentence case. | 1st degree, No feedback yet |
| Count | Shown of total, from the same rows that render. | 3 of 8 intros |
| No search results | "No results", then quote what was searched. | No matches for "zz" |
| Filtered to nothing | Say the filter matched nothing, and offer the way back. | No intros match this filter. · Clear column filters |
| Sort direction | Words people say, for screen readers. Never "ascending". | newest first, highest first |
Accessibility
- SearchInput needs an
aria-labelor a visible label: the placeholder disappears once someone types. - The result count sits in a polite live region, so screen readers hear it change.
- FilterPill and ColumnFilterMenu are menus of radio options, and the trigger's name includes the choice: "Filter by degree: 1st degree".
- ColumnSortButton's name includes the direction in words: "Sort by made, sorted newest first".
- FilterChip says whether it's on (
aria-pressed), and its count is part of its name.
| Keys | What happens |
|---|---|
| Esc | In the search field: clears it. In a filter menu: closes it without changing anything. |
| Enter / Space / ↓ | Opens a filter menu. |
| ↑ / ↓, then Enter | Moves through the options and chooses one. |
| Enter / Space | On a sort header: sorts, or changes the direction. On a FilterChip: turns it on or off. |
Components this uses
Each opens that component’s own page, with its options and code.
| Component | Its job here |
|---|---|
| SearchInput | The search field at the left of the toolbar, with its own clear button. |
| FilterPill | One filter for the whole table, at the right of the toolbar. Filled when it isn't "All". |
| ColumnFilterMenu | A filter in a column's header. Turns blue with a dot when it's on. |
| ColumnSortButton | Sorts by a column from its header, with a blue arrow for the direction. |
| FilterChip | Two or three always-visible views of one feed, each with a count. |
| DataTable | Shows the rows, and keeps its header when filtered to nothing. |
| DataState | The loading, empty and failed bodies under the toolbar. |
| Button | link for "Clear column filters". |
Not decided yet
Proposals for Aman to approve, change or reject. Not rules.
- One "Clear" that resets search and every filter together. Today Bridge OS clears column filters only; search is cleared from its own field.
Where Bridge OS differs today
Follow this page, not the gap.
- The sharing table's search is a hand-made input, not SearchInput.
- The organization People tab uses its own DegreeFilterPill instead of FilterPill.
Sources
- components.md → FilterPill, SearchInput
- components.md → SearchInput (useDebouncedValue 300ms)
- components.md → DegreeFilterPill
- components.md → SearchInput placement
- Bridge OS DESIGN.md → principle 4
- components.md → ColumnSortButton
- Bridge OS
org-tabs.tsx(People tab) - Bridge OS
person-intros-view.tsx;org-tabs.tsx(Intros tab) - Bridge OS
intros-table.tsx(useIntroSort) - Bridge OS
person-intros-view.tsx - Bridge OS
org-tabs.tsx(People tab); Bridge OS DESIGN.md → principle 7 - Bridge OS
degree-filter-pill.tsx,intros-table.tsx(column filters) - Bridge OS
activity-page-content.tsx