# Sort options available via accessible select or listbox

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

## Why it matters

A sort control built from unsemantic divs or buttons fails WCAG 2.2 SC 4.1.2 (Name, Role, Value) and SC 2.1.1 (Keyboard) — keyboard users cannot tab to the control, screen readers cannot identify it as an interactive dropdown, and arrow-key navigation expected of a listbox never fires. Sorting is a primary navigation mechanism in any results page; locking it behind mouse-only interaction excludes a non-trivial portion of users and makes the directory non-compliant in any context where WCAG conformance is required (government contracts, enterprise procurement, ADA-adjacent legal exposure).

## Severity rationale

Low because the failure blocks keyboard and screen reader access to sorting without exposing data or creating a security risk.

## Remediation

Use a native `<select>` element for the sort control — it ships with full keyboard navigation, screen reader support, and no ARIA configuration required. In `components/SortControl.tsx`:

```tsx
<label htmlFor="sort-select">Sort by</label>
<select
  id="sort-select"
  value={currentSort}
  onChange={e => onChange(e.target.value)}
>
  <option value="relevance">Relevance</option>
  <option value="rating">Highest rating</option>
  <option value="distance">Nearest distance</option>
</select>
```

If a custom styled dropdown is required for design reasons, use Radix UI's `<Select>` or shadcn/ui's Select component — both implement the ARIA listbox pattern with correct keyboard navigation. Do not build a custom dropdown from `<div>` or `<button>` elements without the full ARIA combobox/listbox implementation.

## Detection

- **ID:** `sort-a11y`
- **Severity:** `low`
- **What to look for:** Enumerate all relevant files and Examine the sort control. Check whether it uses an accessible `<select>` element, or a custom dropdown with proper ARIA roles (`role="listbox"`, `role="option"`, `aria-expanded`, etc.). Verify it can be navigated via keyboard (arrow keys, Enter to select).
- **Pass criteria:** No more than 0 violations are acceptable. Sort control uses `<select>` or a custom component with proper ARIA roles and keyboard navigation support.
- **Fail criteria:** Sort control is a non-semantic button or styled div with no ARIA attributes, preventing screen reader and keyboard access.
- **Skip (N/A) when:** Results are not sortable or sort control doesn't exist. Signal: no sort control found.
- **Detail on fail:** `"Sort control is a styled button with no <select> or ARIA roles. Screen readers cannot identify it as a dropdown. Keyboard navigation doesn't work."`
- **Remediation:** Use semantic `<select>` for sort:

  ```tsx
  // components/SortControl.tsx
  export function SortControl({ currentSort, onChange }) {
    return (
      <div>
        <label htmlFor="sort-select">Sort by</label>
        <select
          id="sort-select"
          value={currentSort}
          onChange={e => onChange(e.target.value)}
          aria-label="Sort results by"
        >
          <option value="relevance">Relevance</option>
          <option value="rating">Highest rating</option>
          <option value="distance">Nearest distance</option>
        </select>
      </div>
    )
  }
  ```

  For custom dropdowns, use Radix UI's `<Select>` component or similar, which includes full ARIA support.

## External references

- wcag 4.1.2
- wcag 2.1.1

Taxons: accessibility

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