# Boolean/toggle filters render as checkbox or switch with labels

- **Pattern:** `ab-001062` (`directory-search-discovery.facets-filters.boolean-a11y`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001062
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001062)

## Why it matters

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.

## Severity rationale

Low because the failure blocks screen reader and keyboard users from accessing filters without creating a security or data integrity risk.

## Remediation

Render every boolean filter as a native `<input type="checkbox">` wrapped in a `<label>`. In `components/BooleanFilters.tsx`:

```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.

## Detection

- **ID:** `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:

  ```tsx
  // 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.

## External references

- wcag 4.1.2
- wcag 1.3.1

Taxons: accessibility

HTML version: https://auditbuffet.com/patterns/ab-001062
