Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 23 additions & 21 deletions instructions/a11y.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ Comprehensive accessibility rules for web application development. Every anti-pa
| 1.2.1 Audio/Video-only | A | Provide transcript (audio) or text alternative (video). |
| 1.2.2 Captions (Prerecorded) | A | All prerecorded video has synchronized captions. |
| 1.3.1 Info and Relationships | A | Structure (headings, lists, tables, labels, landmarks) programmatically conveyed. |
| 1.3.2 Meaningful Sequence | A | DOM reading order matches visual order. |
| 1.3.2 Meaningful Sequence | A | When the sequence that content is presented affects its meaning, the visual and programmatic ordering of content should align. |
| 1.3.3 Sensory Characteristics | A | Instructions don't rely solely on shape, size, position, or sound. |
| 1.3.4 Orientation | AA | Content not restricted to single orientation unless essential. |
| 1.3.5 Identify Input Purpose | AA | Input fields have `autocomplete` attributes for user data (name, email, tel). |
| 1.3.4 Orientation | AA | Content is not restricted to single orientation unless essential. |
| 1.3.5 Identify Input Purpose | AA | Input fields have `autocomplete` attributes when collecting information about the user. |
| 1.4.1 Use of Color | A | Color is not the only means of conveying info. |
| 1.4.3 Contrast (Minimum) | AA | Text: 4.5:1 normal, 3:1 large (18pt / 14pt bold). |
| 1.4.4 Resize Text | AA | Text resizable to 200% without loss of content. |
| 1.4.10 Reflow | AA | Content reflows at 320px CSS width (no horizontal scroll). |
| 1.4.4 Resize Text | AA | Text resizable to 200% without loss of content or functionality. |
| 1.4.10 Reflow | AA | Sections of content can fit within 320px CSS width viewports without needing to scroll in two-dimensions to read. |
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the Reflow criterion summary, “scroll in two-dimensions” is grammatically incorrect; it should be phrased as “scroll in two dimensions” (or “two-dimensional scrolling”) to avoid a typo in the criteria text.

Suggested change
| 1.4.10 Reflow | AA | Sections of content can fit within 320px CSS width viewports without needing to scroll in two-dimensions to read. |
| 1.4.10 Reflow | AA | Sections of content can fit within 320px CSS width viewports without needing to scroll in two dimensions to read. |

Copilot uses AI. Check for mistakes.
| 1.4.11 Non-text Contrast | AA | UI components and graphics: 3:1 against adjacent colors. |
| 1.4.12 Text Spacing | AA | No loss of content with overridden line-height (1.5x), spacing. |
| 1.4.13 Content on Hover/Focus | AA | Tooltips: dismissible, hoverable, persistent. |
| 1.4.12 Text Spacing | AA | No loss of content or functionality with user-overridden line-height (1.5x), or specified paragraph, letter and word spacing adjustments. |
| 1.4.13 Content on Hover/Focus | AA | Popup content that appears on hover or focus is: dismissible, hoverable, persistent. |

### Operable

Expand All @@ -52,13 +52,13 @@ Comprehensive accessibility rules for web application development. Every anti-pa
| 2.4.4 Link Purpose | A | Link purpose determinable from text or context. |
| 2.4.6 Headings and Labels | AA | Headings and labels describe topic or purpose. |
| 2.4.7 Focus Visible | AA | Keyboard focus indicator is visible. |
| 2.4.11 Focus Not Obscured | AA | Focused element not entirely hidden by sticky headers/footers. *(New in 2.2)* |
| 2.4.11 Focus Not Obscured | AA | Focused element not entirely hidden by other overlaying elements (such as sticky headers or footers). *(New in 2.2)* |
| 2.5.1 Pointer Gestures | A | Multi-point gestures have single-pointer alternative. |
| 2.5.2 Pointer Cancellation | A | Activation on up-event, not down-event. |
| 2.5.3 Label in Name | A | Accessible name contains the visible label text. |
| 2.5.2 Pointer Cancellation | A | Activation on up-event, unless activation can be aborted, reversed or down-event activation is essential. |
| 2.5.3 Label in Name | A | Accessible name contains the label text as it is visually presented. |
| 2.5.4 Motion Actuation | A | Device motion has UI alternative and can be disabled. |
| 2.5.7 Dragging Movements | AA | Drag-and-drop has click/tap alternative. *(New in 2.2)* |
| 2.5.8 Target Size (Minimum) | AA | Touch targets at least 24x24 CSS px. *(New in 2.2)* |
| 2.5.8 Target Size (Minimum) | AA | Interactive controls have a target size, or spacing of at least 24x24 CSS px. *(New in 2.2)* |

### Understandable

Expand Down Expand Up @@ -102,7 +102,7 @@ Comprehensive accessibility rules for web application development. Every anti-pa
## Five Rules of ARIA

1. **Prefer native HTML** — Use `<button>` not `<div role="button">`. Native elements have built-in keyboard, focus, and semantics.
2. **Don't change native semantics** — Don't add `role="heading"` to a `<button>`. Use the correct element.
2. **Don't change native semantics where prohibited** — Don't add `role="heading"` to a `<button>`. Use the correct element.
3. **All ARIA controls must be keyboard operable** — If `role="button"`, handle Enter and Space key events.
4. **Don't use `aria-hidden="true"` on focusable elements** — Hidden from assistive tech but still focusable creates a "ghost" element.
5. **All interactive elements need an accessible name** — Via label, `aria-label`, `aria-labelledby`, or visible text content.
Expand Down Expand Up @@ -147,7 +147,7 @@ Maintain logical nesting: `h1 > h2 > h3 > h4`. Style headings with CSS, not by c

- **Severity**: IMPORTANT
- **Detection**: Pages using only `<div>` without `<nav>`, `<main>`, `<header>`, `<footer>`
- **WCAG**: 1.3.1 (A), 2.4.1 (A)
- **WCAG**: Best practice (supports 1.3.1, 2.4.1)

```html
<!-- GOOD -->
Expand Down Expand Up @@ -230,15 +230,15 @@ Remove redundant ARIA. `<button>` already has `role="button"`.
- **Detection**: `aria-hidden="true"` on focusable elements (button, input, a, [tabindex])
- **WCAG**: ARIA Rule 4

Use `inert` attribute or remove from tab order entirely.
Use `disabled` attribute for allowed elements. Use `inert` attribute for obscured/off-screen content if the content is meant to be completely hidden and not functional.
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This guidance is too broad/incorrect for the detected elements listed above (button, input, a, [tabindex]): disabled is not a valid attribute for anchors or arbitrary focusable elements, and disabling also changes semantics rather than simply preventing focus. Consider revising to explicitly recommend removing focusability (e.g., remove href/tabindex, use hidden/CSS + remove from tab order) or using inert/removing from the DOM when the content must be non-interactive and hidden from assistive tech.

Suggested change
Use `disabled` attribute for allowed elements. Use `inert` attribute for obscured/off-screen content if the content is meant to be completely hidden and not functional.
Do not leave focusable elements inside `aria-hidden="true"` content. For native controls that support it, such as `<button>` or `<input>`, use `disabled` only when disabling the control is the intended behavior. For `<a>` elements or arbitrary elements made focusable with `[tabindex]`, remove focusability instead (for example, remove `href` or `tabindex`). If the content should be completely non-interactive and hidden from assistive technology, use `inert`, `hidden`, or remove it from the DOM.

Copilot uses AI. Check for mistakes.

### A3: Missing Required ARIA Properties

- **Severity**: CRITICAL
- **Detection**: `role="tab"` without `aria-selected`, `role="checkbox"` without `aria-checked`
- **WCAG**: 4.1.2 (A)

Required per role: `tab` needs `aria-selected`/`aria-controls`; `combobox` needs `aria-expanded`/`aria-controls`; `slider` needs `aria-valuemin`/`aria-valuemax`/`aria-valuenow`; `checkbox` needs `aria-checked`.
Required per role: `tab` needs `aria-selected`; `combobox` needs `aria-expanded`/`aria-controls`; `slider` needs `aria-valuemin`/`aria-valuemax`/`aria-valuenow`; `checkbox` needs `aria-checked`.

### A4: Invalid ARIA Role Values

Expand Down Expand Up @@ -279,14 +279,16 @@ Invalid roles are ignored by assistive technology. Common mistakes: `role="input
- **Detection**: `role="presentation"` on interactive elements
- **WCAG**: ARIA Rule 4

Browsers will ignore the presentation role on focusable elements.

### A8: Missing Live Region for Dynamic Content

- **Severity**: IMPORTANT
- **Detection**: Toast/notification components without `role="alert"`, `role="status"`, or `aria-live`
- **WCAG**: 4.1.3 (AA)

```html
<!-- GOOD — content announced when injected -->
<!-- GOOD — content announced when content is injected into a preexisting live region element in the DOM -->
<div role="status" aria-live="polite">Item saved successfully</div>
<!-- Use role="alert" (assertive) for errors -->
<div role="alert">Failed to save. Please try again.</div>
Expand All @@ -302,7 +304,7 @@ Invalid roles are ignored by assistive technology. Common mistakes: `role="input
- **Detection**: `(?:onClick|@click|\(click\))` on `<div>` or `<span>` without keyboard handler
- **WCAG**: 2.1.1 (A)

Use `<button>` instead. If div is required: add `role="button"`, `tabIndex={0}`, and handle Enter/Space.
Use `<button>` instead. If div is required: add `role="button"`, `tabIndex={0}`, and handle Enter/Space as well as expected high contrast styling.
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The phrase “expected high contrast styling” is ambiguous here (it’s not clear what specific requirement to implement). Consider replacing it with concrete, testable guidance (e.g., ensure visible focus styles and contrast for the custom control meet the relevant WCAG criteria) or remove it to avoid unclear instructions.

Suggested change
Use `<button>` instead. If div is required: add `role="button"`, `tabIndex={0}`, and handle Enter/Space as well as expected high contrast styling.
Use `<button>` instead. If a `<div>` is required, add `role="button"`, `tabIndex={0}`, handle Enter and Space, provide a visible focus indicator, and ensure the control's focus and interactive states meet applicable WCAG 2.2 AA contrast requirements.

Copilot uses AI. Check for mistakes.

### K2: Positive `tabindex` Values

Expand All @@ -318,7 +320,7 @@ Only use `tabindex="0"` (add to tab order) and `tabindex="-1"` (programmatic foc
- **Detection**: Modal/overlay without Escape key handler or focus trapping
- **WCAG**: 2.1.2 (A)

Use native `<dialog>` with `showModal()` — it provides focus trapping, Escape-to-close, and focus return automatically. Use `inert` attribute on background content to prevent interaction outside the dialog (96%+ browser support). If custom implementation is needed: trap Tab within the dialog, close on Escape, return focus to the trigger element on close.
Use native `<dialog>` with `showModal()` — it prevents focus from moving to the inert non-dialog content, Escape-to-close, and focus return automatically. If custom implementation is needed: trap Tab within the dialog or use the `inert` attribute for non-dialog content (do not use `inert` on an element that contains the dialog), close on Escape (unless user confirmation of an action is essential), return focus to the trigger element on close, or to best logical location if triggering element is no longer present upon dismissal.

### K4: Missing Skip Link

Expand Down Expand Up @@ -356,13 +358,13 @@ button:focus-visible { outline: 2px solid #005fcc; outline-offset: 2px; }

Pair hover with focus events. Use `onFocus`/`onBlur` alongside `onMouseEnter`/`onMouseLeave`.

### K7: Focus Not Returned After Modal Close
### K7: Focus Not Returned After Custom Modal Close

- **Severity**: IMPORTANT
- **Detection**: Dialog close without restoring focus to trigger
- **Detection**: Custom modal dialog close without restoring focus to trigger
- **WCAG**: 2.4.3 (A)

Store reference to trigger element. On modal close, call `triggerElement.focus()`.
Store reference to trigger element or to best logical location if triggering element is no longer present upon close. On modal close, call `triggerElement.focus()`.
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s trailing whitespace at the end of this line, which can cause noisy diffs and some markdown lint failures. Please remove the extra space at the end of the sentence.

Suggested change
Store reference to trigger element or to best logical location if triggering element is no longer present upon close. On modal close, call `triggerElement.focus()`.
Store reference to trigger element or to best logical location if triggering element is no longer present upon close. On modal close, call `triggerElement.focus()`.

Copilot uses AI. Check for mistakes.

---

Expand Down
Loading