Range filters use accessible input widgets (not freeform text)
Why it matters
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.
Severity rationale
Low because the failure degrades usability and assistive technology access without directly exposing data or enabling exploitation.
Remediation
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.
Detection
-
ID:
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.
External references
- wcag:2.2 · 1.3.5 — Identify Input Purpose
- wcag:2.2 · 4.1.2 — Name, Role, Value
Taxons
History
- 2026-04-18·v1.0.0·Initial import from directory-search-discovery·automated