Glide Data Grid and Canvas Grid Accessibility
Permalink to "Glide Data Grid and Canvas Grid Accessibility"Canvas-rendered grids — Glide Data Grid is a well-known React example — draw every cell as pixels, which lets them scroll millions of rows smoothly. Pixels have no semantics, so a canvas grid is only accessible if it maintains a parallel structure in the DOM that assistive technology can read. Glide Data Grid does this: it renders an accessible table structure off-screen for the visible region and manages focus within it. That covers screen readers and keyboard users reasonably well. What canvas cannot do is respond to the user’s CSS — text spacing overrides, forced colours, minimum font sizes — so those needs must be met in other ways.
This guide belongs to React data grid libraries.
Spec reference
Permalink to "Spec reference"- SC 1.3.1, 4.1.2 (A) — the parallel DOM must expose roles, headers and cell values.
- SC 2.1.1 (A), SC 2.4.7 (AA) — keyboard navigation and a visible focus indicator (drawn on canvas).
- SC 1.4.4 Resize Text (AA) — browser zoom scales canvas; text-only zoom does not.
- SC 1.4.12 Text Spacing (AA) — user stylesheets do not affect canvas text; provide equivalent settings.
- SC 1.4.11 (AA) and forced colours — canvas ignores system colours; offer a high-contrast theme.
When to use — and when not to
Permalink to "When to use — and when not to"Canvas grids make sense for very large or very wide datasets where DOM grids struggle — financial blotters, logs, analytics with hundreds of thousands of rows. Choose one that provides an accessibility layer, and test it.
Do not choose a canvas grid for moderate data where a DOM grid performs fine; you give up user styling, forced colours and native text features for no benefit. The misapplication: a homegrown canvas grid with no parallel DOM, announced by screen readers as a single “graphic”.
Annotated code example
Permalink to "Annotated code example"import DataEditor, { GridCellKind } from '@glideapps/glide-data-grid';
function Blotter({ rows, theme }) {
const getCellContent = useCallback(([col, row]) => {
const r = rows[row];
const v = [r.symbol, r.side, r.qty, r.price][col];
return { kind: GridCellKind.Text, data: String(v), displayData: String(v), allowOverlay: false };
}, [rows]);
return (
<section aria-labelledby="blotter-h">
<h2 id="blotter-h">Trade blotter</h2>
<DataEditor
columns={[{ title: 'Symbol', id: 'symbol' }, { title: 'Side', id: 'side' }, { title: 'Quantity', id: 'qty' }, { title: 'Price', id: 'price' }]}
rows={rows.length}
getCellContent={getCellContent}
theme={theme} // in-app high-contrast and large-text themes
rowHeight={theme.rowHeight} // grows with the user's spacing setting
/>
<p role="status" className="visually-hidden" id="blotter-status"></p>
</section>
);
}
// In-app settings to replace what CSS overrides would normally do (SC 1.4.12, forced colours)
const themes = {
default: { rowHeight: 34, baseFontStyle: '13px', textDark: '#111714', bgCell: '#ffffff' },
spacious: { rowHeight: 48, baseFontStyle: '15px', lineHeight: 1.5, letterSpacing: '0.05em' },
highContrast: { textDark: '#000000', bgCell: '#ffffff', accentColor: '#000000', borderColor: '#000000' },
};
The exact theme properties depend on the library version; the principle is that every user-adjustable aspect CSS would normally provide must be exposed as a setting.
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"| Check | Expected | How to verify |
|---|---|---|
| Tab into grid | Focus to the grid’s accessibility DOM; cell announced | Screen reader output |
| Arrows | Focus moves; canvas draws focus ring; DOM focus follows | Both visuals and speech |
| Screen reader table commands | Work within the rendered region | NVDA Ctrl+Alt+arrows |
| Browser zoom 200% | Canvas re-renders crisply | Visual |
| Text spacing overrides | No effect on canvas | Use in-app spacing setting |
| Forced colours | No effect on canvas | Use in-app high-contrast theme |
Integration context
Permalink to "Integration context"Performance trade-offs that lead teams to canvas are discussed in DOM size limits and accessible performance tradeoffs and pagination versus virtualization for large tables. Text spacing and forced colours testing is covered in text spacing override testing for 1.4.12 and forced-colors screenshots with Playwright.
Gotchas
Permalink to "Gotchas"Copy and find. Browser find (Ctrl+F) cannot search canvas text. Provide an in-grid search with match announcements.
Focus ring. The visible focus indicator is drawn on canvas; verify its contrast in every theme.
Overlay editors. Editors open as DOM overlays; verify their labels and that focus returns to the grid after editing.
Common mistakes
Permalink to "Common mistakes"- Choosing canvas for moderate data.
- No in-app spacing or high-contrast settings.
- Assuming forced colours will “just work”.
- Not testing the parallel DOM with screen readers.
Version and upgrade notes
Permalink to "Version and upgrade notes"Canvas grid libraries update their accessibility layers between versions — what is rendered in the parallel DOM, how focus is handled, which ARIA attributes are set. Re-test screen reader table navigation after upgrades and keep a small set of Playwright tests asserting the accessibility DOM’s roles and names.
Worked example
Permalink to "Worked example"A brokerage moved its blotter from a DOM grid to a canvas grid for performance. Screen reader users found the new grid workable thanks to its accessibility layer, but a trader with low vision who used a high-contrast Windows theme and larger text found the grid unreadable. The team added “Large text” and “High contrast” grid themes, persisted per user, and an in-grid search to replace browser find. The performance gain was kept, and the trader returned to using the main blotter.
Testing checklist
Permalink to "Testing checklist"Design system notes
Permalink to "Design system notes"Document when canvas grids are allowed (data size thresholds) and require the in-app settings and search as part of any canvas grid wrapper.
FAQ
Permalink to "FAQ"Are canvas grids inaccessible?
Not inherently, if they provide a parallel accessibility DOM. They do ignore user CSS settings, which must be replaced by in-app settings.
Does Glide Data Grid support screen readers?
It renders an accessible structure for the visible region and manages focus. Test with your readers and versions.
Why does forced colours not affect the grid?
The browser cannot recolour pixels drawn by script. Offer a high-contrast theme instead.
Can users select and copy text?
Through the grid’s copy features, not native text selection. Provide keyboard copy as in other grids.
Is canvas faster for screen readers?
The accessibility DOM is small (visible rows only), which can help responsiveness for very large datasets.
Should I build my own canvas grid?
Rarely. The accessibility layer is substantial work; prefer a maintained library.
Can screen reader users read rows outside the viewport?
Only rows in the rendered region exist in the accessibility DOM. Scrolling with the keyboard renders more; position is announced via indices. For exhaustive reading, offer export or a paged table view.
Related
Permalink to "Related"- AG Grid React setup — a DOM-based alternative
- Pagination versus virtualization — scaling choices
- Text spacing override testing — what canvas cannot honour