Under Construction: The content on this site draft material. It is not the official content of this site and should not be followed at this time.

Not Legal Advice: The content on this site is provided for informational purposes only and does not constitute legal advice. Consult a qualified attorney for advice specific to your situation.

Purpose

Tables present structured data in rows and columns. Accessible tables use proper header associations so screen readers can announce the column and/or row context for each data cell.

Usage Guidance

  • Use tables only for tabular data, not for layout
  • Every table needs a <caption> or an associated heading
  • Use <th> for header cells and <td> for data cells
  • Use scope="col" or scope="row" on all header cells

Keyboard Support

Tables are not interactive by default. If a table supports sorting, the sort control (a <button> in the header) should follow button keyboard behavior. If cells are focusable, a grid pattern may be appropriate.

Screen Reader Behavior

Screen readers announce the table caption, then read each cell with its associated headers. In a simple table with <th scope="col">, the column header is announced before each cell value.

ARIA Requirements

  • Use <caption> for the table name (preferred over aria-label)
  • scope="col" on column headers
  • scope="row" on row headers
  • aria-sort="ascending|descending|none" on sortable column headers
  • role="grid" only when cells are interactive and need arrow-key navigation

Common Mistakes

  • Using <td> instead of <th> for header cells
  • Missing scope attribute on header cells in complex tables
  • Using layout tables without role="presentation"
  • Adding aria-label to cells instead of using header associations

Live Example

Sample Accessibility Audit Findings
Issue ID WCAG Criterion Severity Status
A-001 1.1.1 Non-text Content Critical Open
A-002 1.4.3 Contrast (Minimum) High In Progress
A-003 2.1.1 Keyboard Critical Resolved
A-004 4.1.2 Name, Role, Value Medium Open

Code Example

<div class="table-wrapper">
  <table>
    <caption>Audit Findings Summary</caption>
    <thead>
      <tr>
        <th scope="col">Issue</th>
        <th scope="col">Criterion</th>
        <th scope="col">Severity</th>
        <th scope="col" aria-sort="none">
          <button type="button">Status</button>
        </th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Missing alt text</td>
        <td>1.1.1</td>
        <td>Critical</td>
        <td>Open</td>
      </tr>
    </tbody>
  </table>
</div>
.table-wrapper { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; }
th { background: #0d2b4e; color: #fff; padding: 0.75rem 1rem; text-align: left; }
td { padding: 0.75rem 1rem; border-bottom: 1px solid #e8ecf0; }
th[aria-sort="ascending"]::after { content: " ↑"; }
th[aria-sort="descending"]::after { content: " ↓"; }