# Accessibility Features and AT Compatibility
taga11y is built around the WAI-ARIA Authoring Practices Guide (APG) [Combobox pattern with autocomplete list](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-autocomplete-list). Accessibility is the primary design constraint, not an afterthought.

## ARIA Pattern Implementation

### Combobox Input
The original `<input>` element is enhanced in place with ARIA attributes:
| Attribute | Value | Purpose |
|---|---|---|
| `role` | `"combobox"` | Identifies the element as a combobox |
| `aria-autocomplete` | `"list"` | Indicates suggestions appear in a list |
| `aria-haspopup` | `"listbox"` | Indicates a listbox popup |
| `aria-expanded` | `"true"` / `"false"` | Reflects dropdown visibility |
| `aria-controls` | `"${base}-listbox"` | Points to the listbox element |
| `aria-activedescendant` | `"${base}-option-{n}"` / `""` | Points to the currently highlighted suggestion |

### Listbox Popup
The suggestion list is a `<ul>` with explicit ARIA roles:
| Attribute | Value | Purpose |
|---|---|---|
| `role` | `"listbox"` | Identifies the container |
| `hidden` | present when closed | Hides suggestions when not open |
| `id` | `"${base}-listbox"` | Linked via `aria-controls` |
Each suggestion is an `<li>` with:
| Attribute | Value | Purpose |
|---|---|---|
| `role` | `"option"` | Identifies as a selectable option |
| `id` | `"${base}-option-{n}"` | Linked via `aria-activedescendant` |
| `aria-selected` | `"true"` / `"false"` | Highlights the active item |

### Chip Region
Selected tags are rendered in a `<div>` with:
| Attribute | Value | Purpose |
|---|---|---|
| `role` | `"list"` | Groups the chips |
| `aria-label` | `"Selected tags"` | Names the chip group |
Each chip is an `<li>` with `role="listitem"` containing a text `<span>` and a remove `<button>`. The remove button has a descriptive `aria-label` (e.g., `"Remove JavaScript"`).

### Error Region
Errors are announced via a visible `role="alert"` region:
| Attribute | Value | Purpose |
|---|---|---|
| `role` | `"alert"` | Announces content assertively to screen readers |
| `hidden` | present when no error | Hides when empty |

### Live Announcer
A visually hidden `aria-live="polite"` region handles routine state announcements (tag added, tag removed, tags cleared, dropdown count, **and the selected-tag summary on input focus entry**). It uses `aria-atomic="true"` to announce the full message each time. The element is positioned off-screen with `clip: rect(0,0,0,0)` rather than `display:none` so it remains in the accessibility tree. The summary-on-focus message is suppressed when the selection is empty so the empty-state focus announcement is unchanged from a widget with no selection ever made.

## Keyboard Navigation
| Key | Action |
|---|---|
| `ArrowDown` | Open dropdown (if closed + input has text) or move to next suggestion (if open). On empty input with static/pre-fetched source: opens and highlights first suggestion. |
| `ArrowUp` | Move to previous suggestion (wraps from first to last). No-op if dropdown is closed. |
| `Home` | Jump to first suggestion (if open). Prevents cursor from moving to start of input. |
| `End` | Jump to last suggestion (if open). Prevents cursor from moving to end of input. |
| `Enter` | Select active suggestion (if open + item highlighted). Commit free text (if open with no active item or closed). |
| `Escape` | Close dropdown, preserve input text and cursor position. |
| `Tab` | Commit pending text if Tab is a delimiter, then move focus out. Never prevented. |
| `Backspace` | Delete character if input has text. Remove last chip if input is empty. |
| Delimiter keys (`,` by default) | Commit current input text as a tag, clear input, prevent delimiter from appearing. |

Single-character delimiter keys also work via on-screen and software keyboards on Android and iOS. The library detects the delimiter either from the `keydown.key` (physical keyboards, Gboard punctuation) or from the resulting `input.value` (FUTO, AOSP-style keyboards that do not surface a usable `keydown` for punctuation). Either path commits the tag and clears the relevant portion of the input. Control delimiters (`Enter`, `Tab`) require `keydown` to fire — keyboards that do not surface a usable `keydown` event for them can still commit by typing a single-character delimiter or by blurring the input.

## Focus Management
- Focus **always stays on the combobox input** during dropdown navigation (`aria-activedescendant`). This preserves natural tab order.
- After chip removal (via remove button, Enter, or Space), focus returns to the combobox input.
- After tag selection from the dropdown, focus stays on the input.
- `Shift+Tab` moves focus out of the component normally — focus is never trapped.
- On component destroy, the original input is restored to its original DOM position with all attributes intact.

## Screen Reader Announcements

### Polite Announcements (via `aria-live="polite"`)
| Event | Message |
|---|---|
| Tag added | `"Tag added: <label>"` |
| Tag removed | `"Tag removed: <label>"` |
| Tags cleared | `"Tags cleared"` |
| Dropdown opened with results | `"N results available"` |
| Dropdown opened empty | `"No results"` |
| Loading starts | `"Loading suggestions…"` |
| Combobox input gains focus with one or more tags selected | `"N tags selected: <label>, <label>, and <label>"` (English default; labels joined via `Intl.ListFormat` for the configured locale) |
| Combobox input gains focus with zero tags selected | no announcement — the AT speaks only the field name |

### Assertive Announcements (via `role="alert"`)
| Event | Message |
|---|---|
| Duplicate tag | `"Already added: <label>"` |
| Whitelist violation | `"Not in the list"` |
| Max tags reached | `"Maximum tags reached"` |
| Fetch failure | `"Failed to load suggestions"` |
Assertive announcements appear as a visible error chip and error region below the chip group, auto-dismissing after 2 seconds.

## Assistive Technology Compatibility
taga11y is designed to work with all major assistive technologies:
| AT | Compatibility Notes |
|---|---|
| **NVDA** (Windows) | Full combobox pattern support. `aria-activedescendant` with listbox announced correctly. |
| **JAWS** (Windows) | Full combobox pattern support. `role="alert"` error region announced on activation. |
| **VoiceOver** (macOS/iOS) | Full combobox pattern support. `aria-activedescendant` navigation works with arrow keys. |
| **TalkBack** (Android) | Full combobox pattern support. Chip remove buttons are individually targetable. |
| **Narrator** (Windows) | Full combobox pattern support. |

## Modality Support

### Keyboard-Only
All interactions are fully operable via keyboard. No pointer-dependent interactions exist. The dropdown opens on `ArrowDown` from the input, and all navigation uses arrow keys.

### Touch/Pointer
Chips, suggestion options, and remove buttons are all pointer-targetable. Each chip's remove button has a ≥24×24 CSS px tap target (WCAG 2.5.8 Level AA), provided by a centered `::before` pseudo-element that expands the hit area without changing the button's layout box or the widget height. Each chip also renders an inset ring (WCAG 2.5.8's sibling SC 1.4.11 Non-text Contrast) so chips are identifiable as distinct UI components against the widget background. Tap a suggestion to select it; tap a remove button to delete the tag.

### Voice Control
Chip remove buttons use unique, descriptive `aria-label` attributes (e.g., `"Remove JavaScript"`) so voice control software can target them by label. All interactive elements have accessible names.

### Switch Device
The component structure supports single-switch scanning: the combobox input is the primary interactive element, followed by the chip region (with individual remove buttons), then the dropdown options when open. No focus trapping ensures switch device users can move forward and backward through the tab order freely.

## Reduced Motion
The `@media (prefers-reduced-motion: reduce)` media query disables all animations and transitions within the component, including the loading spinner, chip appear/dismiss, dropdown open/close, and error region transitions.