Accessible search and product filters for ecommerce stores
Search and filtering are how customers find products on ecommerce stores. When these features are inaccessible, customers who rely on keyboards or screen readers cannot use them effectively. They may be able to reach a category page but cannot narrow 400 products to the 12 that match their needs. They may type into the search field but cannot select from autocomplete suggestions.
Search and filtering components are also technically complex, involving AJAX updates, dynamic content, custom interactive widgets and real-time state management. This complexity means they are rarely built with accessibility as a primary concern and rarely tested with assistive technologies.
Search input accessibility basics
The search input itself is usually a standard text input, and standard text inputs are accessible by default. The common failures are not in the input element itself but in its surrounding context.
- Missing label. The search input must have a programmatically associated label. A placeholder reading "Search..." is not a label. Use either a visible
<label>element or, if the design requires hiding the label visually, anaria-label="Search products"oraria-labelledbypointing to a visually hidden label element. See our ARIA labels guide for details on when to use each approach. - Search button accessible name. The search submit button often contains only a magnifying glass icon. Without a text alternative, screen readers announce "button" with no indication of function. Add
aria-label="Search"to the button, or add visually hidden text inside it:<button type="submit"><svg aria-hidden="true">...</svg><span class="sr-only">Search</span></button>. - Search role. The search landmark (
role="search"or the HTML5<search>element) marks the search form as a navigable landmark. Screen reader users can jump directly to the search form using landmark navigation. Wrap your search form in<search>or addrole="search"to the containing element.
Search autocomplete accessibility with ARIA combobox
Search autocomplete (showing product suggestions, category suggestions or recent searches as the user types) is one of the most complex accessible patterns in ecommerce. The correct ARIA pattern is the combobox pattern.
The combobox pattern requires:
- The search input has
role="combobox",aria-expanded="false"(updated to "true" when suggestions are shown),aria-autocomplete="list"andaria-controlspointing to the suggestions listbox element ID. - When a suggestion is highlighted (via arrow key navigation), the input gets
aria-activedescendantpointing to the ID of the highlighted suggestion element. - The suggestions container has
role="listbox". - Each suggestion has
role="option"and a unique ID. - Keyboard behavior: Down arrow opens the suggestions list (if not already open) and moves focus to the first suggestion. Up/Down arrows navigate between suggestions. Enter selects the highlighted suggestion. Escape closes the suggestions list and returns focus to the input. Tab closes the suggestions list.
Common autocomplete accessibility failures: the suggestions list is keyboard unreachable (opened on input but not navigable by keyboard), the selected suggestion state is communicated only visually without aria-activedescendant, and the suggestions list is not properly associated with the input (making it impossible for screen readers to understand the relationship).
AJAX search results and live region announcements
When a user submits a search and results load on the same page (or on a new page), screen reader users need to know that results have changed. If the search results load via AJAX on the current page, use a live region to announce the result count:
<div role="status" aria-live="polite" aria-atomic="true">24 results for "merino sweater"</div>
The role="status" is equivalent to aria-live="polite" and is semantically appropriate for non-critical status updates. Update this element's text content after the search results load. The announcement should include the result count and ideally the search query.
If the search loads a new page (full page reload), the page title should include the search query and result count: "24 results for 'merino sweater' | Your Store Name". Screen readers announce the page title on page load.
Faceted filter accessibility: checkbox filters
Checkbox-based faceted filters (select one or more values within a filter attribute) are the most common filter pattern in ecommerce. The correct semantic structure:
- Each filter attribute (Size, Color, Brand, Price Range) is a fieldset with a legend:
<fieldset><legend>Size</legend>...</fieldset>. - Individual filter options are standard
<input type="checkbox">elements with associated<label>elements. - If the filter attribute section can collapse and expand, the legend or a button within the fieldset acts as the toggle, with
aria-expandedreflecting the state.
Many filter implementations use custom-styled checkboxes where the native checkbox is visually hidden and a styled span or div provides the visual checkbox appearance. This is acceptable as long as: the native checkbox remains in the DOM and is accessible to keyboard and screen readers (hidden with CSS, not with display: none or visibility: hidden), the visual checkbox state changes with the native checked state, and the label is clickable to toggle the checkbox.
The filter result count shown next to each option ("Blue (42)") should be included in the label or provided via aria-describedby: this context helps screen reader users understand the impact of their selection before making it.
Color swatch filters and accessible names
Color swatch filters (visual color circles or squares used to filter by color) are a common ecommerce pattern that requires specific accessibility treatment.
If swatches are implemented as checkboxes: the checkbox input (visually hidden) needs a label that includes the color name and, ideally, the product count: <label for="color-navy">Navy (18)</label>. The swatch visual provides supplementary information for sighted users; the label provides the accessible name.
If swatches are implemented as buttons (single-select color filter): use the roving tabindex pattern (one active tabindex at a time), and communicate the selected state with aria-pressed="true".
Do not rely on color alone to communicate selection. See our guide on color contrast requirements for WCAG ratios. A selected swatch should have a visual indicator beyond color change: a border, a checkmark or similar. This serves both WCAG 1.4.1 (Use of Color) and users who cannot distinguish the swatch colors (color blind users filtering by color is a real accessibility challenge, but the interface must not make it harder by removing non-color indicators of state).
Price range sliders: accessibility requirements
Custom price range sliders (dual-handle sliders for min and max price) are visually compact and intuitive but require significant work to make accessible.
The correct pattern: use role="slider" on each handle, with aria-valuemin, aria-valuemax, aria-valuenow and aria-valuetext attributes. The aria-valuetext provides a human-readable version of the value: aria-valuetext="$50". Keyboard behavior: arrow keys adjust the value (Left/Down to decrease, Right/Up to increase), Home jumps to minimum, End jumps to maximum.
Dual-handle sliders (both a minimum and maximum handle) require additional care: the two handles should be individually labelable ("Minimum price" and "Maximum price"), and each handle's range constraints should update based on the other handle's position (the maximum handle's minimum value is the current minimum handle value).
Crucially: always provide text input alternatives for the price range. Users who cannot interact with the slider can type specific values. The text inputs also serve users on mobile touch interfaces who find small slider handles difficult to position precisely. Many mobile screen reader users cannot operate custom ARIA sliders reliably.
AJAX filtering and screen reader announcements
When a user selects a filter and the product grid updates via AJAX (without a page reload), several things must happen for accessibility:
- The updated product count is announced via a live region. The announcement should happen after the product grid has loaded: "42 products matching Size: M, Color: Blue."
- Focus is managed appropriately. Three options: focus stays on the filter that was just changed (best for rapid filter selection), focus moves to the product count announcement (good for confirmation), or focus moves to the first product in the updated grid (useful when users are done filtering).
- The loading state is communicated. While the AJAX request is in progress, an announcement like "Loading products..." prevents users from interacting with stale results.
aria-busy="true"on the product grid container indicates that the region is updating.
The wrong approach: silently updating the product grid without any announcement. Screen reader users who are focused in the filter panel have no indication that the results have changed. Users focused on a product card that was removed from the results after filtering experience unexpected focus loss. For guidance on verifying these behaviors, see our screen reader testing guide.
Mobile filter drawers
On mobile, filter panels are often hidden in an off-canvas drawer triggered by a "Filter" button. The drawer must implement the same accessibility patterns as any off-canvas panel or dialog:
- The "Filter" trigger button has
aria-expandedreflecting the drawer state andaria-controlspointing to the drawer container ID. - When the drawer opens, focus moves into it.
- Focus is trapped within the drawer while it is open (the rest of the page is inert).
- A close button is present with a clear accessible name ("Close filters").
- Pressing Escape closes the drawer and returns focus to the "Filter" trigger button.
In mobile filter drawers, the "Apply" button (which closes the drawer and loads filtered results) is a critical accessibility element. Its label should be "Apply filters" rather than just "Apply". After the drawer closes and results update, the live region announces the new product count.
Sort dropdowns
Product sort dropdowns ("Sort by: Featured, Price: Low to High, Newest") are typically native <select> elements, which are keyboard accessible by default. If custom sort dropdowns are used (not native select), they require full ARIA listbox or combobox implementation.
For native select sort dropdowns: the select element needs an associated label. Using a visible label (<label for="sort-select">Sort by</label>) is preferred. The label may be visually present or visually hidden with screen-reader-only CSS; it must be present in both cases.
When the sort selection changes and the product grid updates, announce the updated result order via a live region: "Products sorted by: Price: Low to High."
Clear filter controls
"Clear all filters", "Reset" and individual filter removal controls need accessible names that communicate their purpose.
- "Clear all" is acceptable if no other filters are described on screen. Prefer "Clear all filters" for clarity.
- Individual filter removal buttons (the "X" next to an applied filter chip like "Size: M") need accessible names that include what is being removed:
aria-label="Remove Size: M filter". A standalone "X" or "×" has no accessible name.
Applied filter chips (showing currently active filters) are best rendered as a list with role="list", making it clear to screen reader users how many filters are applied and allowing list navigation.
Search results page semantics
Search results pages need correct semantic structure:
- The search query should be visible on the page: "Results for 'merino sweater'" as a heading or prominent text.
- The result count should be visible and part of the page's accessible content (not just a ARIA live region that announces once on load).
- Results should be in a list structure (
<ul>) so screen reader users know how many results are present and can navigate between them as list items. - Pagination at the bottom of results needs accessible link text: "Page 2" not just "2", or "Next page" not just ">". Ensure all pagination controls are also reachable via keyboard navigation.
No-results states
When a search returns no results or applied filters eliminate all products, the no-results state must be communicated accessibly.
A no-results message should be in the main content area, not hidden or only announced via a live region. It should be specific: "No products found for 'magenta widget'. Try a different search term or browse by category." A generic "No results" is unhelpful.
After AJAX filtering that removes all products, the live region announcement should say "No products match the selected filters" rather than just updating the count to zero and leaving screen reader users to discover the empty grid.
Frequently asked questions
- How do you make ecommerce search autocomplete accessible?
- Use the ARIA combobox pattern: the search input needs role='combobox', aria-expanded, aria-autocomplete='list' and aria-controls pointing to the suggestions listbox. Arrow keys navigate suggestions and Escape closes the list.
- Do AJAX filter updates need to announce results to screen readers?
- Yes. When filters update the product grid via AJAX, use a live region (role='status', aria-live='polite') to announce the updated result count. Without this, screen reader users have no indication that results changed.
- What is the accessible pattern for price range sliders?
- Use role='slider' with aria-valuemin, aria-valuemax, aria-valuenow and aria-valuetext on each handle. Always provide text input alternatives for min and max price, as many screen reader users cannot reliably operate custom sliders.
Complyko tests your store's search and filter flows for WCAG failures, including AJAX updates and mobile filter panels. Get a free report with specific fixes for your platform.
Get your free scan →