Skip to content

Accessibility

A data table is accessible when its structure lives in the markup, not just in the visual grid — a native <table> with a <caption>, a <thead> of <th> header cells, and a scope on each header that binds it to its row or column. With those relationships expressed in the elements, assistive technology announces every value together with the headers that give it meaning, so the table can be read down a column or across a row whether the user is looking at it, listening to it, or navigating it cell by cell. The Table meets WCAG 2.1 AA.

  • Cell text, header labels, and any borders or zebra striping that separate rows must keep accessible contrast on every supported surface, in both the default and contrast schemes. Source: WCAG 1.4.3 Contrast (Minimum).
  • Never convey a value by colour alone — a red figure for a negative number, a green cell for a pass — pair it with a sign, symbol, or label so the meaning survives without colour. Source: WCAG 1.4.1 Use of Color.
  • Keep the table readable when text is enlarged and the layout reflows: wrap a wide table in the overflow utility so columns scroll sideways rather than truncating or overlapping. Source: WCAG 1.4.10 Reflow.
  • Use semantic colour tokens (e.g. foreground/neutral/default, border/neutral/subtle) for text, borders, and striping rather than fixed values, so the grid adapts correctly across themes and surfaces.

Build the table from native elements — <table>, <caption>, <thead>, <tbody>, <th>, and <td> — so browsers and assistive technology derive the row and column relationships for free; a native table needs no ARIA roles to be understood. When a table is wider than its container, wrap it in the overflow utility rather than restructuring it, so it scrolls while the semantics stay intact. The per-element wiring — the caption, the header scopes, and the associations for complex tables — is in the Labelling elements section below. Source: WCAG 1.3.1 Info and Relationships.

Express the structure in the markup so every value carries the headers that name it.

Caption

Place a <caption> as the first child of <table> to give the whole table its accessible name — it is announced before the data, framing what the rows and columns describe. Where a visible caption isn’t wanted, name the table instead with aria-label or aria-labelledby on the <table>.

Column & row headers

Mark every header cell as <th> and set scope="col" or scope="row" so each header is bound to the cells in its column or row. For a table with more than one level of headers, give each <th> an id and list the relevant ids in each data cell’s headers attribute to make the association explicit.

Data cells

<td> cells hold the values and take their context from the headers scoped to them — no per-cell labelling is needed. Keep each value self-describing (fold units into the header rather than repeating them per cell) so it still reads clearly when announced with its headers.

Source: WCAG 4.1.2 Name, Role, Value.