# Accessibility Conformance Report

> WCAG 2.2 Level AA and EN 301 549 conformance report for @toolbox-web/grid — criterion-by-criterion status, linked evidence, and the boundary between what the grid guarantees and what your application must supply.

This is the accessibility conformance report for `@toolbox-web/grid`. It follows the structure of a
VPAT® 2.5, covering both the **WCAG edition** — every Level A and Level AA success criterion in
WCAG 2.2 — and the **EU edition**, which maps those onto the EN 301 549 clauses that the European
Accessibility Act enforces. Every row carries the test that verifies it.

If you are looking for *how to use* the accessibility features, read
[Accessibility](https://toolboxjs.com/grid/guides/accessibility.md) instead. This page is the evidence.

## Report summary

| | |
|---|---|
| **Product** | `@toolbox-web/grid` — the `<tbw-grid>` custom element, its bundled features, and its first-party plugins |
| **Also covers** | `@toolbox-web/grid-angular`, `@toolbox-web/grid-react`, `@toolbox-web/grid-vue` |
| **Standards** | [WCAG 2.2](https://www.w3.org/TR/WCAG22/) Level A and Level AA; [EN 301 549](https://www.etsi.org/deliver/etsi_en/301500_301599/301549/) clauses 5, 9, 11 and 12 |
| **Report editions** | VPAT® 2.5 WCAG edition and EU edition |
| **Conformance claim** | Supports |
| **Evaluation methods** | Automated ([axe-core](https://github.com/dequelabs/axe-core) via Playwright, run against the vanilla demo on every commit), unit and integration tests asserting specific ARIA and interaction behaviour, and manual review with NVDA, JAWS and VoiceOver |
| **Applies from** | v3.6.0 |
| **Report date** | 31 August 2026 |

This page is the authoritative copy and is revised with the product. Print it to PDF from your
browser if you need a file to attach to a procurement pack — that way the copy you hand over
carries the report date above and can never be a stale snapshot of an older release.

### What "the product" means here

A grid is a component, not a page. Several success criteria are about things a component cannot
control — the document's language, the page title, whether there is more than one way to find a
page. Those are marked **Not Applicable** with a note about where the responsibility actually sits.

The **Status** column carries one of the four VPAT® 2.5 values and nothing else — *Supports*,
*Partially Supports*, *Does Not Support*, *Not Applicable* — so the report can be read straight
against the standard's own checklist.

Where the grid provides a conforming mechanism but your content can still break the criterion, the
status stays **Supports**: the term means the product has at least one method that meets the
criterion, and it does. The remark then opens with **Author responsibility** and says exactly which
part is yours. A custom cell renderer that emits an image without `alt` text is the clearest
example.

:::tip[No opt-out]
None of this is configurable, because none of it should be a decision. There is no
`accessibility: false`. Where a requirement risked getting in the way of everyday use, the answer
was to find a less intrusive implementation rather than to add a switch — the 24 px hit targets are
invisible overlays rather than larger buttons, and the truncation tooltip resolves on hover rather
than being stamped onto every cell.
:::

## Level A

| Criterion | Status | How it is met | Verified by |
|---|---|---|---|
| 1.1.1 Non-text Content | Supports | Every icon the grid renders is either labelled or marked `aria-hidden` with adjacent text. **Author responsibility:** images you render from your own data need their own `alt` text. | [accessibility.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/e2e/tests/accessibility.spec.ts) — axe `image-alt`, `svg-img-alt` |
| 1.2.1 Audio-only and Video-only | Not Applicable | The grid renders no time-based media. | — |
| 1.2.2 Captions (Prerecorded) | Not Applicable | The grid renders no time-based media. | — |
| 1.2.3 Audio Description or Media Alternative | Not Applicable | The grid renders no time-based media. | — |
| 1.3.1 Info and Relationships | Supports | The [WAI-ARIA Grid Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/grid/) throughout: `role="grid"` with `aria-rowcount`/`aria-colcount`, `rowgroup`, `row`, `columnheader`, `gridcell`, and `aria-rowindex`/`aria-colindex` on virtualized rows so position survives recycling. | [accessibility.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/e2e/tests/accessibility.spec.ts) — "grid has proper ARIA roles structure"; [aria.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/core/internal/aria.spec.ts) |
| 1.3.2 Meaningful Sequence | Supports | DOM order matches visual order, including pinned columns and grouped headers. | [pinned-columns.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/pinned-columns/pinned-columns.spec.ts) |
| 1.3.3 Sensory Characteristics | Supports | No instruction depends on shape, size or position. | Manual review — no automated check is meaningful |
| 1.4.1 Use of Color | Supports | Sort direction, selection, dirty state and validation errors all carry a non-colour cue. **Author responsibility:** status encoded purely as colour in your own renderers is yours to fix. | [header.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/core/internal/header.spec.ts) — sort indicator; [editing-integration.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/editing/editing-integration.spec.ts) — invalid state |
| 1.4.2 Audio Control | Not Applicable | The grid plays no audio. | — |
| 2.1.1 Keyboard | Supports | Every operation — sort, filter, select, edit, expand, reorder, resize, group, copy, paste — has a keyboard path. Drag-only interactions each have a single-pointer *and* keyboard alternative; see [Pointer Alternatives to Dragging](https://toolboxjs.com/grid/guides/accessibility.md#pointer-alternatives-to-dragging). | [keyboard.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/core/internal/keyboard.spec.ts) |
| 2.1.2 No Keyboard Trap | Supports | <kbd>Tab</kbd> leaves the grid, <kbd>Escape</kbd> leaves an editor and closes any popover. | [accessibility.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/e2e/tests/accessibility.spec.ts) — "focus trap does NOT fight intentional outward Tab" |
| 2.1.4 Character Key Shortcuts | Not Applicable | The grid defines no single-character shortcuts. Every binding uses a modifier or a non-printing key. | — |
| 2.2.1 Timing Adjustable | Not Applicable | The grid imposes no time limit. | — |
| 2.2.2 Pause, Stop, Hide | Not Applicable | Nothing moves, blinks or auto-updates on its own. Row animations are transitions triggered by your data changing, and they disable under `prefers-reduced-motion`. | [row-animation.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/core/internal/row-animation.spec.ts) |
| 2.3.1 Three Flashes or Below Threshold | Supports | Nothing in the grid flashes. | Manual review — no automated check is meaningful |
| 2.4.1 Bypass Blocks | Not Applicable | Page-level. Worth noting that the grid is a *single* tab stop — a roving-tabindex grid does not become a block of hundreds of stops that a user needs to bypass. | [accessibility.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/e2e/tests/accessibility.spec.ts) — "tab order moves through grid regions correctly" |
| 2.4.2 Page Titled | Not Applicable | Page-level; supplied by your application. | — |
| 2.4.3 Focus Order | Supports | Focus follows grid structure, and is deliberately restored after sorting, filtering, editing and row deletion rather than being dropped to `<body>`. | [accessibility.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/e2e/tests/accessibility.spec.ts) — "focus survives sort reorder", "focus survives scroll (virtualization)"; [focus-manager.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/core/internal/focus-manager.spec.ts) |
| 2.4.4 Link Purpose (In Context) | Not Applicable | The grid renders no links of its own. Links inside your renderers are yours. | — |
| 2.5.1 Pointer Gestures | Supports | Every path-based gesture (column resize, column and row reorder, tool-panel resize, range selection) has a single-pointer, non-path alternative. | [reorder-plugin.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/reorder-columns/reorder-plugin.spec.ts), [row-drag-drop.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/row-drag-drop/row-drag-drop.spec.ts), [resize.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/core/internal/resize.spec.ts), [range-selection.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/selection/range-selection.spec.ts) |
| 2.5.2 Pointer Cancellation | Supports | Actions fire on the up-event. The down-event only moves focus, which is reversible and has no side effect. | [pointer-drag.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/core/internal/pointer-drag.spec.ts) |
| 2.5.3 Label in Name | Supports | Where a control has visible text, its accessible name starts with that text. | [accessibility.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/e2e/tests/accessibility.spec.ts) — axe `label-content-name-mismatch` |
| 2.5.4 Motion Actuation | Not Applicable | No function is operated by device motion. | — |
| 3.1.1 Language of Page | Not Applicable | Page-level; supplied by your application. | — |
| 3.2.1 On Focus | Supports | Moving focus never changes context. | [focus-manager.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/core/internal/focus-manager.spec.ts) |
| 3.2.2 On Input | Supports | Changing a value commits it to the row; it never navigates or reconfigures the page. | [editing-integration.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/editing/editing-integration.spec.ts) |
| 3.2.6 Consistent Help | Not Applicable | Page-level; the grid provides no help mechanism. | — |
| 3.3.1 Error Identification | Supports | A cell that fails validation gets `aria-invalid="true"` and a `title` carrying the message, alongside the visual state — so the error reaches assistive technology, not only CSS. | [editing-integration.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/editing/editing-integration.spec.ts) — "invalid cells" |
| 3.3.2 Labels or Instructions | Supports | An injected editor takes its accessible name from its column header unless it already names itself, so it is never announced as a bare "edit text". | [editor-injection.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/editing/internal/editor-injection.spec.ts) — "Accessible name" |
| 3.3.7 Redundant Entry | Not Applicable | The grid runs no multi-step process. | — |
| 4.1.2 Name, Role, Value | Supports | Verified by the axe-core scan on all four framework demos, plus targeted tests for sort state, expansion state, selection state and editor naming. | [accessibility.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/e2e/tests/accessibility.spec.ts) — "no WCAG 2.2 AA violations axe can detect"; [editor-injection.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/editing/internal/editor-injection.spec.ts) |

:::note
SC 4.1.1 Parsing is not listed: it was removed in WCAG 2.2 and is always considered satisfied.
:::

## Level AA

| Criterion | Status | How it is met | Verified by |
|---|---|---|---|
| 1.2.4 Captions (Live) | Not Applicable | The grid renders no time-based media. | — |
| 1.2.5 Audio Description | Not Applicable | The grid renders no time-based media. | — |
| 1.3.4 Orientation | Supports | Nothing is locked to portrait or landscape. | Manual review — the grid sets no orientation lock |
| 1.3.5 Identify Input Purpose | Supports | The grid edits arbitrary records, not information about the user. **Author responsibility:** where a column *does* collect the user's own data, set `autocomplete` on your editor. | — author responsibility |
| 1.4.3 Contrast (Minimum) | Supports | The default light and dark themes meet 4.5:1 on every text pair, and `dg-theme-contrast.css` / `dg-theme-large.css` reach the Level AAA 7:1. **Author responsibility:** a theme of your own is yours to verify. | [accessibility.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/e2e/tests/accessibility.spec.ts) — axe `color-contrast` runs against the shipped default theme. The AAA figure for the contrast and large themes is from computed-ratio review, not CI |
| 1.4.4 Resize Text | Supports | All internal sizing is `em`-based off a single `--tbw-font-size`, so the grid scales with the user's font size without clipping. | [variables.css](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/core/styles/variables.css) plus the text-spacing e2e below, which resizes text and asserts no clipping |
| 1.4.5 Images of Text | Supports | The grid renders text as text; its icons are SVG or glyphs, not pictures of words. | Manual review — no automated check is meaningful |
| 1.4.10 Reflow | Supports | Nothing around the table needs a second scrollbar at 320 px: the tool panel is clamped to its container, the header truncates its title before its controls. The table itself uses the SC's explicit two-dimensional-layout exception for data tables, and the [Responsive plugin](https://toolboxjs.com/grid/plugins/responsive.md) offers card mode where you would rather it reflowed. See [Reflow and text spacing](https://toolboxjs.com/grid/guides/accessibility.md#reflow-and-text-spacing). | [accessibility.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/e2e/tests/accessibility.spec.ts) — "the page does not scroll horizontally at 320px", "the tool panel stays inside the grid it docks into" |
| 1.4.11 Non-text Contrast | Supports | Focus ring, borders, and other non-text UI clear 3:1 against the grid background in both light and dark. | Computed-ratio review of the theme tokens in [variables.css](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/core/styles/variables.css); axe does not check non-text contrast |
| 1.4.12 Text Spacing | Supports | Rows measure their rendered content, so user spacing overrides make rows taller instead of clipping them. Horizontally, a cell whose text no longer fits reveals the full value on hover; `--tbw-cell-white-space: normal` removes truncation entirely. | [accessibility.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/e2e/tests/accessibility.spec.ts) — "user text spacing neither clips nor overlaps grid content", "text the spacing pushes out of view is still readable in full"; [event-delegation.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/core/internal/event-delegation.spec.ts) — "truncated text reveal" |
| 1.4.13 Content on Hover or Focus | Supports | Tooltips are dismissible with <kbd>Escape</kbd>, hoverable, persistent, and appear on keyboard focus. | [tooltip.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/tooltip/tooltip.spec.ts) |
| 2.4.5 Multiple Ways | Not Applicable | Page-level; supplied by your application. | — |
| 2.4.6 Headings and Labels | Supports | Column headers describe their columns; every control has a descriptive accessible name. | [accessibility.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/e2e/tests/accessibility.spec.ts) — axe `button-name`, `aria-command-name` |
| 2.4.7 Focus Visible | Supports | A 2 px `outline` focus ring that survives forced-colors mode. | [accessibility.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/e2e/tests/accessibility.spec.ts) — "focus-visible indicators exist on focusable elements" |
| 2.4.11 Focus Not Obscured (Minimum) | Supports | Plugins that overlay the rows viewport report the band they cover, and keyboard navigation scrolls the focused cell clear of it rather than underneath — [sticky rows](https://toolboxjs.com/grid/plugins/sticky-rows.md) vertically, [pinned columns](https://toolboxjs.com/grid/plugins/pinned-columns.md) horizontally. | [sticky-rows.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/sticky-rows/sticky-rows.spec.ts), [pinned-columns.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/pinned-columns/pinned-columns.spec.ts), [keyboard.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/core/internal/keyboard.spec.ts) |
| 2.5.7 Dragging Movements | Supports | All ten drag surfaces have a single-pointer alternative, shipped in the same bundle unit as the drag it replaces so conformance never depends on an optional plugin. | [reorder-plugin.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/reorder-columns/reorder-plugin.spec.ts), [row-drag-drop.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/row-drag-drop/row-drag-drop.spec.ts), [range-selection.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/selection/range-selection.spec.ts), [resize.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/core/internal/resize.spec.ts), [pivot-panel.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/pivot/pivot-panel.spec.ts) |
| 2.5.8 Target Size (Minimum) | Supports | Every control answers a pointer across at least 24 × 24 px, on a mouse as well as a finger. Small controls reach it with a transparent overlay, so visual density is unchanged. Raise `--tbw-touch-target-min` to 44 px for the Level AAA criterion. | [accessibility.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/e2e/tests/accessibility.spec.ts) — "small grid controls answer a pointer across a 24px square", "the tool panel splitter answers a pointer across a 24px square" |
| 3.1.2 Language of Parts | Not Applicable | The grid introduces no language changes of its own. | — |
| 3.2.3 Consistent Navigation | Not Applicable | Page-level; supplied by your application. | — |
| 3.2.4 Consistent Identification | Supports | The same control means the same thing in every column, plugin and framework adapter. | [cross-framework-visual.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/e2e/tests/cross-framework-visual.spec.ts) — parity across all four adapters |
| 3.3.3 Error Suggestion | Supports | The grid displays and announces whatever your validator returns. **Author responsibility:** writing a message that suggests a correction is your part. | [editing-integration.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/editing/editing-integration.spec.ts) — "invalid cells" |
| 3.3.4 Error Prevention | Supports | Edits are cancelable via the `cell-commit` event, and the [UndoRedo plugin](https://toolboxjs.com/grid/plugins/undo-redo.md) reverses committed changes. **Author responsibility:** confirmation for legal, financial or data-deleting actions belongs in your application. | [undo-redo.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/plugins/undo-redo/undo-redo.spec.ts) |
| 3.3.8 Accessible Authentication (Minimum) | Not Applicable | The grid performs no authentication. | — |
| 4.1.3 Status Messages | Supports | Sort applied, filter cleared, rows selected, data loaded, edit committed — all announced through the grid's built-in `aria-live` region without moving focus. Configurable and translatable; see [Configuring Announcements](https://toolboxjs.com/grid/guides/accessibility.md#configuring-announcements). | [accessibility.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/e2e/tests/accessibility.spec.ts) — "sort action populates aria-live region", "row selection populates aria-live region", "data reload populates aria-live region"; [aria.spec.ts](https://github.com/OysteinAmundsen/toolbox/blob/main/libs/grid/src/lib/core/internal/aria.spec.ts) |

## EN 301 549 — EU edition

[EN 301 549](https://www.etsi.org/deliver/etsi_en/301500_301599/301549/) is the harmonised European
standard that the [European Accessibility Act](https://ec.europa.eu/social/main.jsp?catId=1202) and
the Web Accessibility Directive enforce. It incorporates WCAG by reference, so the tables above
carry most of it — but it adds requirements WCAG has no equivalent for, and those are what this
section covers.

Clauses carry the same statuses, and the same **Author responsibility** marker, as the tables
above. Clauses that do not apply to a software component at all — two-way voice (6), video
capabilities (7), hardware (8), non-web documents (10), relay and emergency services (13) — are
omitted rather than padded out with repetition.

### Clause 5 — Generic requirements

| Clause | Status | Notes |
|---|---|---|
| 5.1 Closed functionality | Not Applicable | The grid runs in a browser with the user's own assistive technology attached. Nothing about it is closed. |
| 5.2 Activation of accessibility features | Supports | There is nothing to activate. Every accessibility feature is on, unconditionally, with no configuration flag that turns it off. |
| 5.3 Biometrics | Not Applicable | The grid uses no biological characteristic. |
| 5.4 Preservation of accessibility information during conversion | Not Applicable | The grid performs no format conversion. Export to CSV and Excel emits data, not a presentation format with accessibility information to preserve. |
| 5.5.1 Operable parts — means of operation | Supports | Every operable part is reachable and operable by keyboard, and by a single pointer without a path-based gesture. |
| 5.5.2 Operable parts — discernibility | Supports | Operable parts are discernible visually and non-visually: each has an accessible name and role, and none relies on colour alone. |
| 5.6.1 Locking or toggle controls — tactile or auditory status | Not Applicable | The grid presents no physical locking control. |
| 5.6.2 Locking or toggle controls — visual status | Supports | Toggle state is exposed programmatically (`aria-pressed`, `aria-expanded`, `aria-sort`, `aria-selected`) and visually, not by colour alone. |
| 5.7 Key repeat | Not Applicable | The grid neither implements nor overrides key repeat; the platform's own setting applies. |
| 5.8 Double-strike key acceptance | Not Applicable | Platform-level. |
| 5.9 Simultaneous user actions | Supports | No operation requires more than one simultaneous action. Multi-select accelerators (<kbd>Ctrl</kbd>-click, <kbd>Shift</kbd>-click) are shortcuts, never the only path — the same selections are reachable by single keypresses and by the selection checkbox column. |

### Clause 9 — Web

| Clause | Status | Notes |
|---|---|---|
| 9.1 Perceivable · 9.2 Operable · 9.3 Understandable · 9.4 Robust | Supports | These clauses restate the WCAG Level A and AA success criteria one for one. See the [Level A](#level-a) and [Level AA](#level-aa) tables above for the criterion-by-criterion status and evidence. |
| 9.5 Non-interference | Supports | The four non-interference criteria — 1.4.2 Audio Control, 2.1.2 No Keyboard Trap, 2.2.2 Pause Stop Hide, 2.3.1 Three Flashes — are met even on pages where the grid is embedded in content that does not itself conform. The grid plays no audio, traps no focus, animates nothing autonomously, and flashes nothing. |
| 9.6 WCAG conformance requirements | Supports | The grid satisfies the conformance requirements it can: complete processes, only accessibility-supported technologies, and no interference. **Author responsibility:** "full pages" is by definition a property of your page, not of a component on it. |

### Clause 11 — Software

Clause 11 applies when the grid is embedded in something that is not a public web page — an Electron
or Tauri desktop application, or a WebView inside a native app. The requirements largely mirror
clause 9, so only the software-specific additions are listed.

| Clause | Status | Notes |
|---|---|---|
| 11.1–11.4 (Perceivable, Operable, Understandable, Robust) | Supports | Mirror the WCAG criteria; see the tables above. |
| 11.5.2.3 Row, column and headers | Supports | `aria-rowindex`, `aria-colindex`, `aria-rowcount` and `aria-colcount` are set on virtualized rows, so a screen reader reports the true position in the dataset rather than the position in the recycled DOM window. |
| 11.5.2.5 Modification of focus and selection attributes | Supports | Focus and selection are programmatically settable through the public API, not only by pointer or keyboard. |
| 11.5.2.6 Label relationships | Supports | Header-to-cell relationships come from the ARIA grid pattern; editors take their accessible name from their column header. |
| 11.5.2.7 Parent-child relationships | Supports | Grouped rows, tree rows and master-detail rows upgrade the rows body to `role="treegrid"` and carry `aria-level`, `aria-setsize`, `aria-posinset` and `aria-expanded`, so hierarchical position is exposed even though the DOM is flat. |
| 11.5.2.8 Text | Supports | All cell content is real text in the DOM and is available to the accessibility API. No cell value is painted to a canvas or baked into an image. |
| 11.5.2.9 List of available actions · 11.5.2.10 Execution of available actions | Supports | Actions are exposed as real controls with roles, and every one can be executed programmatically as well as by pointer or keyboard. |
| 11.5.2.11 Tracking of focus and selection attributes | Supports | Focus and selection changes fire events and update ARIA state, so assistive technology can follow them. |
| 11.5.2.12 Modification of text | Supports | Editors are standard form controls; assistive technology can read and write their value. |
| 11.5.2.13 Modification of values and text by assistive technology | Supports | Values are edited through native inputs, which AT can drive directly. |
| 11.5.2.14 Modification of focus cursor | Supports | The focused cell is settable via the API; the roving `tabindex` follows it. |
| 11.5.2.15 Change notification | Supports | The `aria-live` region announces sort, filter, selection, load and commit; see SC 4.1.3 above. |
| 11.6 Documented accessibility usage | Supports | Every keyboard path, pointer alternative and announcement is documented in [Accessibility](https://toolboxjs.com/grid/guides/accessibility.md) and in the per-plugin pages. |
| 11.7 User preferences | Supports | The grid inherits the platform's colour scheme, contrast, font size and reduced-motion preferences rather than overriding them: `light-dark()` for scheme, `em`-based sizing off the inherited font size, `prefers-reduced-motion` on every transition, and `forced-colors` support on the focus ring. |
| 11.8 Authoring tool | Not Applicable | The grid edits records in your data model. It does not author ICT content, so 11.8.1–11.8.5 do not apply. |

### Clause 12 — Documentation and support services

| Clause | Status | Notes |
|---|---|---|
| 12.1.1 Accessibility and compatibility features | Supports | Documented in [Accessibility](https://toolboxjs.com/grid/guides/accessibility.md) and in this report, both published as ordinary web pages. |
| 12.1.2 Accessible documentation | Supports | The documentation site is HTML built by [Starlight](https://starlight.astro.build/), navigable by keyboard and screen reader, and is also published in plain markdown at [`/llms.txt`](/llms.txt) and [`/llms-full.txt`](/llms-full.txt). |
| 12.2.2 Information on accessibility and compatibility features | Supports | Support runs through [GitHub issues](https://github.com/OysteinAmundsen/toolbox/issues), which is a public, accessible channel. Accessibility defects are treated as P0. |
| 12.2.3 Effective communication · 12.2.4 Accessible documentation | Supports | The project communicates in writing through GitHub, which supports assistive technology. **Author responsibility:** a commercial support desk, where you offer one, is yours. |

:::caution[A conformance report is not a certification]
This is a self-assessment, published in the VPAT® structure because that is what procurement asks
for. It has not been through a third-party audit. Where your procurement process requires one,
this report gives an auditor a starting point rather than replacing them.
:::

## How this report is verified

Claims in this report are not self-assessed prose. Each one is backed by a check that runs in CI:

- **`e2e/tests/accessibility.spec.ts`** — the axe-core scan under the WCAG 2.2 AA ruleset, run
  against the vanilla demo, asserting zero violations at any impact. The same file holds the
  targeted gates: focus management and focus-trap behaviour, target size probed by hit-testing the
  four corners of a 24 px square, reflow at 320 px, and text spacing applied exactly as the success
  criterion states it.
- **Unit and integration tests** co-located with the source — ARIA attribute correctness, live-region
  announcements, keyboard handling, editor naming, and validation state.
- **Manual review** — NVDA on Windows, JAWS on Windows, VoiceOver on macOS and iOS. Automated tools
  find roughly a third of accessibility defects; the rest need a person.

### Why one demo covers the adapters

The scan runs against the vanilla demo because that is where the accessibility tree is produced.
The Angular, React and Vue packages contribute none of it: they set no `role`, no `aria-*` and no
`tabindex` anywhere in their source. What they do is mount framework-rendered content *inside* the
cell the grid has already built and labelled, behind a `display: contents` wrapper that carries no
role and no accessible name. The roles, indices, sort state and live-region announcements an
assistive technology sees are byte-identical across all four demos, because one implementation
emits them.

The one behaviour the adapters do add is registering overlay editors as external focus containers,
so the grid's focus trap treats a date picker or dropdown as part of the grid. A static scan cannot
observe that, so it is gated by unit tests co-located with each adapter instead —
`use-grid-overlay.spec.tsx` (React), `use-grid-overlay.spec.ts` (Vue) and
`base-overlay-editor.spec.ts` (Angular).

Automated scanning has known blind spots, and two rules are deliberately suppressed in the scan
config for reasons documented there: `scrollable-region-focusable` (virtualization recycles rows
outside the viewport) and `aria-required-children` (the grid uses `role="presentation"` layout
wrappers between `role="grid"` and `role="rowgroup"`, which is spec-legal but confuses the rule).

## What is still on you

Conformance is a property of the finished page, and a component can only get you most of the way:

1. **Cell renderers** — the grid cannot add `alt` text to an image it did not create, or a name to a
   button you rendered yourself.
2. **Custom themes** — if you override the colour tokens, re-check contrast. Start from
   `dg-theme-contrast.css` if you need a guaranteed floor.
3. **Validation messages** — the grid announces what your validator returns. Make it say how to fix
   the problem.
4. **Page-level criteria** — title, language, landmarks, skip links, and consistent navigation.
5. **Column widths** — a column narrow enough to truncate most of its values is technically
   conformant (the text is still reachable) but is a poor experience. Prefer widths that fit, or let
   cells wrap.

## Feedback

Accessibility bugs are treated as P0. If something here is wrong, or you hit a barrier this report
does not mention, please
[open an issue](https://github.com/OysteinAmundsen/toolbox/issues/new) and include the assistive
technology, browser and operating system you used, and what was announced — or what wasn't.
