Sheath rule
a11y-table-headers
Tables must have proper header cells for accessibility.
- Package
- Core
- Category
- Accessibility
- Default severity
- error by default
- Auto-fix
- Manual fix
#Why
Data tables need <th> elements to be accessible to screen readers. Headers help users understand the relationship between data cells and their column/row labels. Without headers, tables become difficult to navigate and understand.
#Examples
#Bad
<!-- Table without headers -->
<table>
<tr>
<td>Name</td>
<td>Email</td>
<td>Role</td>
</tr>
<tr>
<td>John</td>
<td>john@example.com</td>
<td>Admin</td>
</tr>
</table>
#Good
<!-- Table with header row -->
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Role</th>
</tr>
</thead>
<tbody>
<tr>
<td>John</td>
<td>john@example.com</td>
<td>Admin</td>
</tr>
</tbody>
</table>
<!-- Table with scope for complex tables -->
<table>
<tr>
<th scope="col">Name</th>
<th scope="col">Email</th>
</tr>
<tr>
<th scope="row">John</th>
<td>john@example.com</td>
</tr>
</table>
<!-- Presentation table (layout only, no headers needed) -->
<table role="presentation">
<tr>
<td>Column 1</td>
<td>Column 2</td>
</tr>
</table>
#Options
Use these options to control header requirements and data-table detection.
| Option | Type | Default | Description |
|---|---|---|---|
requireScope |
boolean | false |
When true, <th> elements must have a scope attribute |
minRows |
integer | 2 |
Minimum static row count that makes a headerless table a data-table candidate |
<?php
'a11y-table-headers' => ['error', [
'requireScope' => true,
'minRows' => 3,
]],
#Scope Values
Choose the scope value that describes the cells controlled by a header.
| Value | Use For |
|---|---|
col |
Column headers |
row |
Row headers |
colgroup |
Headers spanning multiple columns |
rowgroup |
Headers spanning multiple rows |
When requireScope is enabled, a present static value must be one of these four
keywords (case-insensitive). Empty and unknown values are reported; dynamic
Blade scope values are left for runtime.
#Notes
- Use
role="presentation"orrole="none"for layout tables (they won't be flagged) - Single-row tables are skipped (likely layout tables)
- Use
<thead>,<tbody>, and<tfoot>for additional structure idandheadersassociate cells explicitly, for tables whose structurescopecannot describe- Tables, rows, and headers excluded by
hidden,inert, oraria-hidden="true"do not contribute to the analysis - A conflicting
none/presentationrole on a focusable or globally named<th>is ignored by the browser, so the native header semantics remain - Conditional or dynamic accessibility-tree exclusion is not checked when its row and header outcomes cannot be determined
#References
- WCAG 1.3.1 Info and Relationships - Level A success criterion for table structure
- WebAIM: Creating Accessible Tables - Best practices for data table accessibility
#Related Rules
- a11y-form-label - Labels for form controls