Filtering & Sorting (Faceted Navigation)
Master the interaction patterns, logic models, and accessibility requirements that make large catalogs navigable without dead ends or cognitive overload.
8 min read
Monstera
Plant · Premium
Snake plant
Plant · Budget
Pruning shears
Tool · Budget
Watering can
Tool · Premium
Terracotta pot
Pot · Budget
Ceramic planter
Pot · Premium
Fiddle-leaf fig
Plant · Premium
Trowel
Tool · Budget
Filters apply instantly, show their active state, expose a count, and offer a one-click reset — and an empty result still explains what happened.
The full lesson
Once a catalog grows beyond a few dozen items, browsing becomes slow and painful. Search alone is not enough. Filtering and sorting close that gap — they let users narrow a large, mixed result set down to exactly what they need. Done well, they feel invisible. Done poorly, they produce dead ends, stale states, and a UI that feels broken even when the data is fine.
Why Filtering Is a Logic Problem First
Every filter system hides an implicit logic model. When a user selects Brand: Nike AND Color: Red, do the results show only red Nike products, or all Nike products plus all red products? Most users expect AND across facets and OR within a single facet — but teams rarely document this, and often ship the wrong behavior.
Before designing the UI, write out the filter logic explicitly:
- Within a facet: OR (selecting Size: M and Size: L returns items available in either size)
- Across facets: AND (Size: M AND Color: Red returns only items that are both medium and red)
- Range inputs: inclusive (Price: $20–$60 includes items at exactly $20 and $60)
- Hierarchical facets: selecting a parent includes all its children, unless a child is explicitly deselected
The danger here is silence — the UI looks the same whether the logic is right or wrong, but the results are confusing. Validate the logic model with users before you build.
Facet Selection: What to Expose and What to Hide
Facets (the filter categories you show) must come from your content model, not be invented by the UI team. A facet is only worth showing if it has high discrimination power — meaning it splits the result set into sub-groups that users actually care about.
Steps to find high-quality facets:
- Audit your content for structured attributes — every metadata field is a candidate
- Validate with user research: which attributes do users actually filter by? Card sorting and tree testing surface this efficiently
- Calculate discrimination power: a facet where 90% of items share the same value is useless
- Remove facets that apply to fewer than about 15% of items in context — they create confusing empty states
Common high-value facets by domain:
| Domain | High-value facets |
|---|---|
| E-commerce | Category, Price, Brand, Size, Color, Rating, Availability |
| Job board | Location, Job type, Salary range, Experience level, Date posted |
| Media library | Format, Topic, Date, Author, Duration |
| SaaS admin | Status, Assigned user, Date range, Tag, Priority |
| Research database | Publication type, Date, Author, Journal, Subject |
Filter UI Patterns
Checkboxes and Multi-Select
Checkboxes are the default for most categorical facets. They signal that users can select multiple values at once. Show the item count next to each option — this previews the effect of a selection and helps users choose strategically. Counts must reflect the current active filter state, not the full unfiltered catalog.
Range Sliders
Use range sliders for continuous numeric attributes: price, date, distance, file size. Always pair a slider with a text input. Drag handles lack the precision users need for specific values and are nearly impossible to use with a keyboard alone. A custom range slider must replicate native keyboard behavior: arrow keys move the handle, and Home/End jump to the bounds.
Applied Filter Chips
Every active filter must appear as a removable chip above the result list. This gives users a clear view of their active constraints and lets them remove individual filters without reopening the filter panel. Include a “Clear all” action next to the chips. Without visible applied filters, users lose track of why the results look the way they do.
Sort Controls
Sorting and filtering do different jobs. Filtering narrows the set; sorting sequences the same set. Keep them visually and spatially separate. Place sort controls near the result count — for example, “Showing 84 results” — as a dropdown or segmented control. Common sort options:
- Relevance (default for search-driven results)
- Newest / Oldest
- Price: low to high / high to low
- Rating: high to low
- Alphabetical
Do not default to alphabetical sort on catalog pages. It is rarely how users think about items and produces poor outcomes for discovery.
Do
Don't
Preventing Dead Ends
The hardest design challenge in faceted navigation is preventing zero-result states that users cannot predict or escape. Three strategies work together to prevent this:
Disable unavailable values. When an active filter makes a facet value impossible (selecting it would return zero results), visually disable that value. Communicate its status to assistive technology via aria-disabled="true". Add a tooltip or inline note explaining why it is disabled.
Show live counts. Display the number of matching results next to each facet value, updated to reflect the current filter state. A count of zero is a strong enough signal that most users will not select it. Consider hiding or disabling zero-count values altogether.
Escape paths on zero results. If the user still reaches a zero-result state, the page must not be a dead end. Offer: a suggestion to remove a filter (ideally the most restrictive one), a “Clear all filters” action, and entry points to broader browsing. Showing just “No results found” with nothing else leaves users stranded.
Mobile Filtering Patterns
A persistent sidebar does not work on narrow screens. Two patterns dominate:
Bottom sheet / modal. A “Filter” button — often with a badge showing the active count, like “Filter (3)” — opens a full-screen or partial-screen overlay listing all facets. The user makes selections and taps “Show N results” to commit and close. The confirm-before-apply pattern works well here. It prevents the jarring experience of results updating live behind a modal sheet.
Horizontal scrollable filter chips. A row of chips representing primary filter categories scrolls horizontally above the results. Each chip opens a focused bottom sheet for that single facet. This works best with five or fewer high-priority facets. A final “More” chip opens a full filter sheet.
Avoid collapsing a desktop sidebar into a small accordion on mobile. The interaction model changes — a scannable panel on a 1200 px canvas becomes an overwhelming list on a 390 px screen.
On mobile, WCAG 2.2 SC 2.5.8 (Target Size Minimum, AA) requires interactive targets of at least 24 x 24 CSS pixels. Facet checkboxes and filter chips in compact designs frequently fall short. Enlarge the hit area with padding even if the visible indicator is smaller.
Accessibility Requirements
WCAG 2.2 AA is the legal baseline in most jurisdictions as of 2026. Key requirements specific to filtering UI:
- Live region for result count. When filters change the result set, announce the updated count via
aria-live="polite". This tells screen reader users that results changed without requiring them to navigate down the page. - Keyboard operability. Every checkbox, slider, range input, chip, and button must work by keyboard. Custom components that look like checkboxes but are built as divs must fully implement the ARIA checkbox role and keyboard pattern.
- Focus management. When the user applies a filter, keep focus on the control they just used. Do not move focus to the results list. The
aria-liveannouncement handles feedback, and jumping focus disorients keyboard users. - Descriptive labels. Facet groups need a visible heading or
aria-label. “Size” as a group label is sufficient. Avoid generic labels like “Option 1”. - State communication. Active filter chips must communicate their selected state:
aria-pressed="true"for toggle patterns, or a visually distinct and programmatically determinable selected state.
Using outline: none on focus rings without a replacement violates WCAG 2.2 SC 2.4.11 (Focus Not Obscured) and SC 2.4.7 (Focus Visible). Every interactive filter element must have a visible focus indicator.
Real-Time vs. Confirm-and-Apply
Two models exist for applying filters:
Real-time (immediate) updates. Every filter interaction immediately updates the result set. Users see results change as they refine. This is the dominant pattern on desktop e-commerce. It works well when the back-end responds within about 200 ms (the Doherty Threshold — the point at which users experience a system as responding instantly). Use a skeleton screen or subtle loading indicator while results fetch, not a full-page spinner that blocks interaction.
Confirm-and-apply. Users configure filters and then explicitly tap or click “Apply” or “Show N results” before the result set changes. This suits mobile (where accidental touches are common), slow APIs, or complex filter UIs where users need to make several selections together before seeing results. The trade-off is one extra interaction step.
Hybrid approaches are also valid. Apply high-confidence single-select filters in real-time, but batch multi-select changes with a small “Apply” button that appears after any selection is made.
Design Token and State Considerations
Filter components have more states than most UI patterns, and each state must be designed explicitly:
- Default (unfocused, unselected)
- Hovered
- Focused (keyboard)
- Selected / active
- Disabled (would produce zero results)
- Loading (result count is updating)
In a token-driven system (W3C DTCG format with primitive-to-semantic-to-component tiers), these states map to component-level tokens like filter-chip-background-selected, which references a semantic token like color-interactive-selected, which in turn references a primitive OKLCH value. This chain keeps light-mode and dark-mode variants consistent without per-component color overrides. OKLCH is the modern choice for perceptually uniform color scales — HSL-derived palettes produce inconsistent perceived brightness across hues.
When a filter chip transitions from unselected to selected, animate only compositor-friendly properties (opacity, transform). Animating a background-color change is fine. Animating layout properties like width or height in response to a reordering result list is not — it degrades to janky reflow. If you reorder the result list, consider using the View Transitions API (now broadly supported in 2026) to smoothly transition items instead of an abrupt reorder.