# Accessibility specification: [name of the component]

> Template version 1.0, 2026-10-07, from https://auricartisan.com/templates/component-spec/
> Licence: CC0 1.0 (https://creativecommons.org/publicdomain/zero/1.0/). Copy it, change it and use it for anything; no credit needed.
> Write this before the component is built, and keep it with the component in your design system.
> Replace everything in [square brackets], delete the parts that do not apply, then delete this note.

| Spec | |
|---|---|
| Component | [Name, as the design system calls it] |
| Spec version | [1.0] |
| Status | [Draft / In review / Agreed] |
| Owner | [Name, team] |
| Last reviewed | [YYYY-MM-DD] |
| Design | [Link to the design file] |
| Code | [Link to the code, or "Not built yet"] |
| Standard | [WCAG 2.2, Level AA] |

## 1. Purpose and when to use it

[One or two sentences: what the component does for the person using it.]

- Use it when: [...]
- Do not use it when: [...]. Use [another component] instead.

## 2. Anatomy

[Name every part, so the design, the code and this spec use the same words. Add a labelled drawing if you have one.]

| Part | What it is | Required |
|---|---|---|
| [Trigger] | [The button that shows and hides the panel] | [Yes] |
| [Label] | [The visible text on the trigger] | [Yes] |
| [Icon] | [A chevron that turns when the panel opens; decoration only] | [No] |
| [Panel] | [The content that is shown and hidden] | [Yes] |

## 3. Variants and states

Variants: [sizes, styles or layouts, such as small and default, or with and without an icon]

| State | How it looks | How assistive technology is told |
|---|---|---|
| Default | [...] | [...] |
| Hover | [...] | [Not exposed] |
| Focus | [The focus indicator: color, thickness, offset] | [Not exposed; the browser tracks focus] |
| Pressed or expanded | [...] | [aria-pressed="true" or aria-expanded="true"] |
| Selected or checked | [...] | [aria-selected="true" or aria-checked="true", or the native checked state] |
| Disabled | [...] | [The disabled attribute, or aria-disabled="true" if it must stay focusable] |
| Error | [...] | [aria-invalid="true", with the message linked by aria-describedby] |
| Loading | [...] | [aria-busy="true", or a status message] |

[Delete the states the component does not have, and add any it has.]

## 4. Semantics

Start from a native HTML element. Use ARIA only where no native element does the job. A role changes what assistive technology is told, not how the element behaves, so with a role you must also build the keyboard support in section 5 yourself.

| Item | Value |
|---|---|
| Element | [<button type="button">] |
| Role | [None: the element's own role. Otherwise the ARIA role, such as tablist] |
| Accessible name | [Where the name comes from: the visible label, aria-labelledby or aria-label] |
| Description | [Optional: aria-describedby pointing at hint or error text] |
| States and properties | [Such as aria-expanded, aria-selected, aria-checked, aria-controls] |
| Relationships | [Which element labels, describes or controls which] |

- The name contains the words of the visible label, best at the start (2.5.3 Label in Name, Level A).
- Structure and relationships shown visually are in the markup too (1.3.1 Info and Relationships, Level A).
- Every control has a name and a role, and its states can be read and set by assistive technology (4.1.2 Name, Role, Value, Level A).

## 5. Keyboard

Everything that works with a pointer works with a keyboard (2.1.1 Keyboard, Level A), and focus can always leave the component with the keyboard (2.1.2 No Keyboard Trap, Level A).

| Key | Where focus is | What it does |
|---|---|---|
| Tab | [Before the component] | [Moves focus to [the trigger]] |
| Shift+Tab | [...] | [...] |
| Enter | [On the trigger] | [...] |
| Space | [On the trigger] | [...] |
| Escape | [...] | [...] |
| Arrow keys | [...] | [...] |
| Home / End | [...] | [...] |

[If the component follows a pattern in the WAI-ARIA Authoring Practices Guide (APG), start from its keyboard table and note any difference, with the reason. Avoid keys the browser or screen readers already use. A shortcut that is a single letter, number or symbol can be turned off or changed, or works only while the component has focus (2.1.4 Character Key Shortcuts, Level A).]

## 6. Focus management

| Moment | Where focus goes | Why |
|---|---|---|
| On open | [Such as the first field in the dialog, or it stays on the trigger] | [...] |
| On close | [Such as back to the control that opened it] | [...] |
| On delete | [Such as the next item, the previous one if it was the last, or the list's heading if the list is now empty] | [...] |
| On error | [Such as the first field with an error, or an error summary] | [...] |
| When content loads or changes | [Usually nowhere: focus stays put and a status message says what changed] | [...] |

- Focus moves in an order that keeps the meaning and the way it works (2.4.3 Focus Order, Level A).
- The focus indicator can always be seen (2.4.7 Focus Visible, Level AA).
- A focused element is never entirely covered by sticky headers, banners or other content you added (2.4.11 Focus Not Obscured (Minimum), Level AA).

## 7. Screen reader announcements

[What a screen reader user hears, and when. Most of it comes from the name, role and state in section 4. Write down what you expect, then confirm it in testing: the exact words differ between screen readers.]

| When | What is announced | Where it comes from |
|---|---|---|
| Focus reaches [the trigger] | [Its name, role and state, such as "Delivery options, button, collapsed"] | [The name, the role and aria-expanded] |
| [The panel opens] | [The new state, such as "expanded"] | [aria-expanded changes to true] |
| [Results update after a filter] | [Such as "[number] results"] | [A status message in a live region (4.1.3 Status Messages, Level AA)] |

Use a live region only for a change that happens away from focus and that the person needs to know about. Put the live region in the page before its text changes, keep each message short, and prefer polite (role="status") to assertive (role="alert").

## 8. Pointer and touch

- Target size: each target is at least 24 by 24 CSS pixels (2.5.8 Target Size (Minimum), Level AA). In brief, a smaller target is allowed only when:
  - it has room around it: a circle 24 CSS pixels across, centred on it, does not touch another target or another small target's circle;
  - another control on the same page does the same thing and is big enough;
  - it sits inside a sentence or a block of text;
  - its size is set by the browser and you have not changed it;
  - that size is essential, or required by law.
- [Our size: [44 by 44] CSS pixels. 2.5.5 Target Size (Enhanced), Level AAA, asks for 44 by 44.]
- Actions happen when the pointer is released, not when it goes down, so a slip can be cancelled by moving away first (one way to meet 2.5.2 Pointer Cancellation, Level A).
- Anything done by dragging can also be done with single clicks or taps (2.5.7 Dragging Movements, Level AA).
- A gesture with several fingers or a path has a single-pointer alternative (2.5.1 Pointer Gestures, Level A).
- Content that appears on hover or focus can be dismissed without moving the pointer or focus, can itself be hovered, and stays until it is dismissed or no longer relevant (1.4.13 Content on Hover or Focus, Level AA).

## 9. Color and contrast

- Text: at least 4.5:1 against its background, or 3:1 for large text, which is at least 24 px, or 18.66 px and bold (1.4.3 Contrast (Minimum), Level AA).
- Parts: the edges, icons and state indicators a person needs to find the control and see its state have at least 3:1 against the colors next to them, and so does the focus indicator (1.4.11 Non-text Contrast, Level AA).
- Not color alone: a state shown by color is also shown another way, such as text, an icon, an underline or a change of shape (1.4.1 Use of Color, Level A).
- Check every state in every theme: [light, dark, Windows contrast themes].

| Pair | Foreground | Background | Ratio | Needs |
|---|---|---|---|---|
| [Label text] | [#...] | [#...] | [...:1] | [4.5:1] |
| [Border of the control] | [#...] | [#...] | [...:1] | [3:1] |
| [Focus indicator] | [#...] | [#...] | [...:1] | [3:1] |
| [Selected state] | [#...] | [#...] | [...:1] | [3:1] |

## 10. Motion

- When the reduced motion setting is on (prefers-reduced-motion: reduce), replace movement with an instant change or a short fade.
- Motion started by an interaction can be turned off, unless it is essential (2.3.3 Animation from Interactions, Level AAA).
- Anything that moves, blinks or scrolls by itself for more than five seconds, next to other content, can be paused, stopped or hidden (2.2.2 Pause, Stop, Hide, Level A).
- Nothing flashes more than three times in one second (the simplest way to meet 2.3.1 Three Flashes or Below Threshold, Level A).
- [Animations in this component: what moves, for how long, and what replaces it when motion is reduced.]

## 11. Content and language

- Labels: [the visible label of each control, and the accessible name if it adds to the label]. Labels and headings say what they are for (2.4.6 Headings and Labels, Level AA), and inputs have labels or instructions (3.3.2 Labels or Instructions, Level A).
- Errors: each error is described in text and says what is wrong (3.3.1 Error Identification, Level A) and, where you can, how to fix it (3.3.3 Error Suggestion, Level AA).

| Error | When it shows | Message |
|---|---|---|
| [Empty required field] | [On submit] | [Enter your [field name]] |
| [...] | [...] | [...] |

- Default text: [button text, empty states and hints the component brings with it], in plain words.
- Other languages: text in a language other than the page's is marked with lang (3.1.2 Language of Parts, Level AA).
- Right-to-left: with dir="rtl" the layout mirrors. Use logical CSS properties, such as margin-inline-start rather than margin-left. Icons that point a direction, such as back and forward arrows, flip; others stay as they are. [Say whether Left and Right arrow keys swap in a right-to-left layout.]

## 12. Test cases

Each test has an expected result. Run them before the component is released, and again after any change to it.

### Keyboard

- [ ] Tab reaches [the trigger]; the focus indicator can be seen and is not covered.
- [ ] Every key in section 5 does what the table says.
- [ ] Focus goes where section 6 says on open, close, delete and error.
- [ ] Focus never gets stuck, and moves in the reading order.

### Screen reader

Test with at least [NVDA with Firefox or Chrome on Windows] and [VoiceOver with Safari on macOS or iOS].

- [ ] On focus, the name, role and state are announced as section 7 says.
- [ ] Each change of state is announced: [...].
- [ ] Decorative parts, such as [the chevron], are not announced.

### Zoom and reflow

- [ ] At 200% zoom nothing is cut off, overlaps or stops working (1.4.4 Resize Text, Level AA).
- [ ] At 320 CSS pixels wide, the same as 400% zoom in a 1280 px window, it works without scrolling in two directions (1.4.10 Reflow, Level AA).
- [ ] With the text spacing of 1.4.12 Text Spacing (Level AA) applied, nothing is cut off, hidden or stops working.
- [ ] In a Windows contrast theme, the edges, the focus indicator and every state can still be seen.

### Pointer and touch

- [ ] Every target is at least [24 by 24] CSS pixels, or meets one of the exceptions in section 8.
- [ ] [Anything done by dragging also works with single clicks or taps.]

### Automated

- [ ] [axe-core, or your own checker] reports no violations in any state from section 3.
- [ ] Unit tests check the roles, names and states in section 4.

Automated checks find only some problems. They do not replace the tests above.

## 13. Related patterns and references

- Pattern on Auric Artisan: [https://auricartisan.com/accessibility-patterns/[slug]/]
- WAI-ARIA Authoring Practices Guide: [https://www.w3.org/WAI/ARIA/apg/patterns/[pattern]/]
- WCAG 2.2 success criteria that apply: [numbers and names]
- Design: [link]. Code: [link]. Tests: [link]. Acceptance criteria: [link].

## Change log

| Version | Date | Change | By |
|---|---|---|---|
| [1.0] | [YYYY-MM-DD] | [First version] | [Name] |
