# Pathrule Pattern: Web Accessibility (1.0.0)
# ::pathrule:package:web-accessibility

### [RULE] Native HTML before ARIA  (path: /src/components)
<!-- scope: folder | priority: high | strict -->

Reach for the native element that already has the role, state, and keyboard behavior you need before adding any ARIA. Per the 2026 WebAIM Million report, pages with ARIA average more detectable errors than pages without it, so ARIA is a last resort, not a default.

- Use `<button>` for actions and `<a href>` for navigation. Never attach `onClick` to a `<div>` or `<span>` to fake a control.
- Use `<label>`, `<fieldset>`/`<legend>`, `<nav>`, `<main>`, `<header>`, and `<footer>` instead of generic `<div>` plus `role`.
- Reserve ARIA for custom widgets that have no native equivalent (`role="dialog"`, `role="tablist"`, `aria-live`), and never override a native role with a redundant one such as `<button role="button">`.
- Any ARIA control built from non-interactive elements must add `tabindex="0"` plus keydown handlers, because ARIA declares semantics but adds zero keyboard behavior.

---

### [RULE] Keyboard operability and visible focus  (path: /src/components)
<!-- scope: folder | priority: high | advisory -->

All functionality must work with the keyboard alone, and the focused element must always be visibly distinguishable. This covers WCAG 2.2 AA 2.1.1 Keyboard, 2.4.7 Focus Visible, 2.4.11 Focus Not Obscured, 2.5.7 Dragging Movements, and 2.5.8 Target Size.

- Never remove the focus outline with `outline: none` unless you replace it with an equally visible indicator at 3:1 contrast against adjacent colors.
- Maintain a logical DOM/tab order and avoid positive `tabindex` values; use `tabindex="-1"` only for programmatic focus targets.
- Manage focus on route changes, dialog open/close, and dynamic content so focus is never lost or trapped unintentionally.
- Provide a non-drag alternative for any drag-and-drop interaction, and give pointer targets at least 24x24 CSS pixels (44x44 for primary touch targets).

---

### [MEMORY] Accessible forms: labels, errors, and grouping  (path: /src/components)

Every input needs a programmatic label and a clear path from field to error. Placeholder text is not a label and disappears on input, so it never substitutes for `<label>`.

- Associate labels explicitly with `<label htmlFor={id}>` matching the input `id`, or wrap the input inside the `<label>`.
- Mark required fields with the native `required` attribute and surface validation errors with `aria-invalid="true"` plus `aria-describedby` pointing at the error text node.
- Group related controls (radio sets, address blocks) in `<fieldset>` with a `<legend>`; use `aria-live="polite"` for async/inline validation summaries.
- Keep error text adjacent and specific ("Enter a valid email"), and move focus to the first invalid field on submit failure.

---

### [MEMORY] Accessibility tooling and CI gates  (path: /src)

Automated checks catch roughly a third of accessibility issues, so we run them in CI and pair them with manual keyboard and screen-reader passes. The target is WCAG 2.2 AA.

- `eslint-plugin-jsx-a11y` runs in lint and blocks the build on errors (clickable non-interactive elements, missing alt, label-less controls).
- `@axe-core/react` logs violations to the console in development; `jest-axe` / `axe-core` assert no violations in component tests.
- Color contrast must meet 4.5:1 for normal text and 3:1 for large text and UI/focus indicators; verify in design tokens, not per component.
- Automation never replaces manual testing: tab through every flow and verify with a real screen reader (NVDA, VoiceOver, or JAWS), since simulators are unreliable.

---

### [SKILL] web-accessibility-review  (path: /)

---
name: web-accessibility-review
description: Run this checklist before merging any new or changed UI component to verify WCAG 2.2 AA. Covers semantic HTML, keyboard operability, focus, labels, contrast, and screen-reader output.
---

# Web accessibility review

- [ ] Every interactive element uses a native control (`<button>`, `<a href>`, `<input>`); no `onClick` on `<div>`/`<span>`.
- [ ] ARIA is present only where native HTML cannot express the role or state, with no redundant or conflicting roles.
- [ ] All functionality is reachable and operable with the keyboard alone; tab order follows reading order.
- [ ] Focus is always visible (no bare `outline: none`) and meets 3:1 contrast against adjacent colors.
- [ ] Focus is managed on dialog open/close and route changes, and is never trapped unintentionally.
- [ ] Every form field has an associated `<label>`; errors use `aria-invalid` plus `aria-describedby`.
- [ ] Images have meaningful `alt`, or `alt=""` when decorative; icon-only buttons have an accessible name.
- [ ] Text contrast is at least 4.5:1 (3:1 for large text and UI components).
- [ ] Pointer targets are at least 24x24 CSS px and drag interactions have a single-pointer alternative.
- [ ] Verified with `eslint-plugin-jsx-a11y`, an axe run, and one manual screen-reader pass.
