Range filters rendered as freeform text inputs (<input type="text">) violate WCAG 2.2 SC 1.3.5 (Identify Input Purpose) and SC 4.1.2 (Name, Role, Value) — screen readers cannot convey the expected value type, and users relying on step controls or browser autofill get nothing. Beyond accessibility, freeform text introduces validation surface area: users enter "10-50", "$10 to $50", or "ten dollars" and the backend must parse all variants or return unexpected results. Semantic number or range inputs constrain input to a machine-readable format without custom parsing.
Low because the failure degrades usability and assistive technology access without directly exposing data or enabling exploitation.
Replace freeform text range inputs with paired <input type="number"> elements, each carrying an explicit label and aria-label. In components/PriceFilter.tsx:
<label htmlFor="price-min">Minimum price</label>
<input
id="price-min"
type="number"
value={min}
onChange={e => setMin(e.target.value)}
aria-label="Minimum price"
/>
<label htmlFor="price-max">Maximum price</label>
<input
id="price-max"
type="number"
value={max}
onChange={e => setMax(e.target.value)}
aria-label="Maximum price"
/>
Alternatively, use shadcn/ui's Slider component, which ships with full ARIA support out of the box.
ID: directory-search-discovery.facets-filters.range-filter-a11y
Severity: low
What to look for: Enumerate all relevant files and Identify any range filters (e.g., price range, distance, rating). Check whether they use accessible HTML input widgets: <input type="range"> (slider), <input type="number"> (number inputs), or a custom accessible component with ARIA attributes. Avoid freeform text inputs for ranges where possible.
Pass criteria: Range filters use <input type="range"> (slider), <input type="number"> (min/max inputs), or an accessible custom component with proper ARIA labels and semantic HTML.
Fail criteria: Range filters are freeform text inputs (e.g., <input type="text"> with placeholder "e.g., 10-50") or have no accessibility labels/ARIA attributes.
Skip (N/A) when: Directory has fewer than 3 filterable attributes (see Global N/A Rule), or no range filters exist (only categorical filters).
Detail on fail: "Price range filter uses a freeform text input. Should use <input type='range'> or separate number inputs for min/max." or "Range input lacks aria-label. Cannot be identified by screen readers."
Remediation: Implement range filters with appropriate HTML and ARIA:
// components/PriceFilter.tsx
import { useState } from 'react'
export function PriceFilter({ onFilterChange }) {
const [min, setMin] = useState(0)
const [max, setMax] = useState(100)
return (
<div>
<label htmlFor="price-min">Minimum price</label>
<input
id="price-min"
type="number"
value={min}
onChange={e => {
setMin(e.target.value)
onFilterChange({ min: e.target.value, max })
}}
aria-label="Minimum price"
/>
<label htmlFor="price-max">Maximum price</label>
<input
id="price-max"
type="number"
value={max}
onChange={e => {
setMax(e.target.value)
onFilterChange({ min, max: e.target.value })
}}
aria-label="Maximum price"
/>
</div>
)
}
Or use a slider component from an accessible UI library like shadcn/ui or Radix UI.