Boolean filters rendered as clickable divs or unsemantic buttons fail WCAG 2.2 SC 4.1.2 (Name, Role, Value) and SC 1.3.1 (Info and Relationships) — a screen reader user lands on a div that says "Has WiFi" with no indication it is togglable, interactable, or currently active. This eliminates the filtering experience entirely for keyboard-only and assistive technology users. Beyond accessibility, div-based toggles break browser default behaviors: they don't receive focus in the correct tab order, don't respond to Space or Enter as checkboxes do, and don't participate in form submission.
Low because the failure blocks screen reader and keyboard users from accessing filters without creating a security or data integrity risk.
Render every boolean filter as a native <input type="checkbox"> wrapped in a <label>. In components/BooleanFilters.tsx:
<label className="flex items-center gap-2">
<input
type="checkbox"
checked={filters.hasWifi || false}
onChange={() => toggleFilter('hasWifi')}
/>
<span>Has WiFi</span>
</label>
Native checkboxes handle Space/Enter toggling, :checked state, and screen reader announcements without custom ARIA. If you need a styled toggle switch, use shadcn/ui's Switch component, which wraps a checkbox with full role="switch" semantics.
ID: directory-search-discovery.facets-filters.boolean-a11y
Severity: low
What to look for: Enumerate all relevant files and Identify any boolean or toggle filters (e.g., "Has WiFi", "Open now", "Wheelchair accessible"). Check whether they render as <input type="checkbox"> or <input type="checkbox" role="switch"> with associated labels, or as a switch component from an accessible UI library. Avoid buttons or click-to-toggle divs.
Pass criteria: Boolean filters render as <input type="checkbox"> or accessible switch components with associated labels. Each checkbox/switch is labeled so its purpose is clear.
Fail criteria: Boolean filters are rendered as clickable divs/buttons without checkbox semantics or labels.
Skip (N/A) when: Directory has fewer than 3 filterable attributes (see Global N/A Rule), or no boolean/toggle filters exist.
Detail on fail: "'Open now' filter is a clickable button with no checkbox or switch semantics. Screen reader users cannot identify it as toggleable." or "Checkbox for WiFi filter has no label."
Remediation: Render boolean filters as semantic checkboxes:
// components/BooleanFilters.tsx
export function BooleanFilters({ filters, onFilterChange }) {
const toggleFilter = (key) => {
onFilterChange({ ...filters, [key]: !filters[key] })
}
return (
<div className="space-y-3">
<label className="flex items-center gap-2">
<input
type="checkbox"
checked={filters.hasWifi || false}
onChange={() => toggleFilter('hasWifi')}
/>
<span>Has WiFi</span>
</label>
<label className="flex items-center gap-2">
<input
type="checkbox"
checked={filters.isOpenNow || false}
onChange={() => toggleFilter('isOpenNow')}
/>
<span>Open now</span>
</label>
</div>
)
}
Ensure each checkbox is paired with a <label> for proper accessibility and usability.