Accessibility
DzGrid follows the WAI-ARIA grid pattern (a treegrid for trees and groups) and aims at WCAG 2.2 AA. The draft
conformance report is
docs/a11y/acr-wcag22.md: it says,
criterion by criterion, what is checked and how. Its screen-reader listening is not done yet, so the criteria that
rest on what a screen reader says are marked « partially supports » until then.
What the grid does
Section titled “What the grid does”- One tab stop. Tab enters the grid at the focused cell; the arrows, Page Up / Down, Home / End and Ctrl+Home / End move inside; Tab leaves. The focus is never lost when rows are sorted, filtered, reloaded or scrolled away (rows are virtualised: the focused row is kept).
- Roles and states.
role="grid"(ortreegrid), real row and column counts (aria-rowcount,aria-colcount) even when only some rows are drawn,aria-sorton sorted headers,aria-selectedandaria-expandedon rows,aria-readonlywhere editing is on. - Announcements. Sorting, filtering, a page change, an export, a refused edit, a row moved are said through a live region, in the grid’s language, with plurals.
- Every action has a keyboard way: column moves and sizes, row moves (Alt+Shift+arrows), the fill (Ctrl+D / Ctrl+R), the dock’s edges and tabs, the menus (Shift+F10 or the Menu key). Dragging is never the only way.
- Targets of 24 px at least; contrast of 4.5:1 for every text and 3:1 for every part, checked by script on
every colour pair, light and dark; forced colours (Windows High Contrast) use the system’s colours; motion
follows
prefers-reduced-motion. - Zoom and text size. Page zoom to 400 % works; at 320 CSS pixels wide nothing scrolls sideways outside the grid; rows grow with the reader’s default text size.
Try it on the grid below with the keyboard only: Tab into it, move with the arrows, Enter on a title sorts, Shift+F10 opens a title’s menu, Space selects a row.
What is left to you
Section titled “What is left to you”- A name. Give the
labelsetting (label="Invoices",[label]in Angular), or every grid on the page has the same generic name (« Data grid »). - Translated texts. Headers are your strings, already in the user’s language; the grid’s own sentences come from
messages(English, French and Arabic ship). A part’s own texts go through its context’smsg('app.key'). - The page’s language and direction (
langanddiron<html>): the grid followsdirection: 'auto'and says its sentences in itsmessages’ language. - Custom parts keep their promises. A cell part draws inside a cell the grid owns (role, focus, keys stay the
grid’s); a button in it must be a real
<button type="button">with a name. Do not move the focus yourself from a part; say things with the context’sannounce(text). - Colours only through the theme’s variables (
--tz-*,--dzg-*), so the contrast checks and the dark and forced-colours modes keep working. Do not convey meaning by colour alone: abadgepart writes its text too. - Problems. A
Problemnever carries row data (rows may be personal), only a code, a path and a short detail: log them, do not show them to the user as they are.
import { createGrid } from '@dzgrid/dom';import { EN } from '@dzgrid/dom/locales/en';
type Lighthouse = { id: string; name: string; range: number };
document.documentElement.lang = 'en';document.documentElement.dir = 'ltr';
const grid = createGrid<Lighthouse>(document.getElementById('lighthouses')!, { rows: [{ id: 'p1', name: 'Mistcape Light', range: 22 }], columns: [{ id: 'name', header: 'Lighthouse' }, { id: 'range', header: 'Range (nmi)', type: 'number' }], getRowId: (p) => p.id, messages: EN, locale: 'en-GB', label: 'Lighthouses of the coast', direction: 'auto',});
grid.api.announce('List updated');import { Component } from '@angular/core';import { DzGrid, DzgController, type DzgColumnOrGroup } from '@dzgrid/angular';import { EN } from '@dzgrid/dom/locales/en';
type Lighthouse = { id: string; name: string; range: number };
@Component({ selector: 'app-lighthouses', imports: [DzGrid], template: ` <dz-grid [rows]="rows" [columns]="columns" [getRowId]="rowId" [messages]="messages" locale="en-GB" label="Lighthouses of the coast" direction="auto" (gridReady)="ctrl = $event" /> <button type="button" (click)="ctrl?.announce('List updated')">Refresh</button>`,})export class Lighthouses { readonly rows: Lighthouse[] = [{ id: 'p1', name: 'Mistcape Light', range: 22 }]; readonly columns: DzgColumnOrGroup<Lighthouse>[] = [{ id: 'name', header: 'Lighthouse' }, { id: 'range', header: 'Range (nmi)', type: 'number' }]; readonly rowId = (p: Lighthouse) => p.id; readonly messages = EN; ctrl: DzgController<Lighthouse> | null = null;}<dz-grid locale="en-GB" label="Lighthouses of the coast" direction="auto"></dz-grid>import '@dzgrid/element/define';import type { DzGridElement } from '@dzgrid/element';import { EN } from '@dzgrid/dom/locales/en';
type Lighthouse = { id: string; name: string; range: number };
const grid = document.querySelector<DzGridElement<Lighthouse>>('dz-grid')!;grid.columns = [{ id: 'name', header: 'Lighthouse' }, { id: 'range', header: 'Range (nmi)', type: 'number' }];grid.getRowId = (p) => p.id;grid.messages = EN;grid.rows = [{ id: 'p1', name: 'Mistcape Light', range: 22 }];
await grid.ready;grid.grid?.api.announce('List updated');The owner’s hand-made listening route (keyboard and screen readers, the essential path) is
docs/a11y/parcours-essentiel.md.