There are many semantic HTML tags covered under WCAG SC 1.3.1: Info and Relationships, but in this article, we’ll zoom in on one of the most misunderstood, the table.
You might be thinking: “Do we really need tables when we already have semantic tags like lists?”
It’s a valid question. Lists (<ul>, <ol>, <dl>) are fantastic for presenting structured information, but when data grows beyond a simple one-to-one relationship, tables step in as the powerhouse.
Lists vs. Tables: When Does a Table Make Sense?
Imagine you want to display personal details. Using a description list, the information looks clean and simple:
<h2>Personal Details</h2>
<dl>
<dt>Full Name</dt>
<dd>Tom Henry</dd>
<dt>Date of Birth</dt>
<dd>01-Jan-1999</dd>
<dt>Gender</dt>
<dd>Male</dd>
<dt>Country</dt>
<dd>Australia </dd>
<dt>Company</dt>
<dd>Google</dd>
</dl> It would look like this:
That’s great for a single person’s data. But what if you have multiple people’s details?
A list would stretch the page endlessly, making it harder for users (especially screen reader users) to locate specific information. This is where a table becomes the clear winner:
<table>
<thead>
<tr>
<th>Full Name</th>
<th>Date of Birth</th>
<th>Gender</th>
<th>Country</th>
<th>Company</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tom Henry</td>
<td>01-Jan-1999</td>
<td>Male</td>
<td>Australia</td>
<td>Google</td>
</tr>
<tr>
<td>Stifen Watson</td>
<td>02-Jan-2000</td>
<td>Male</td>
<td>U.S.</td>
<td>TPGi</td>
</tr>
</tbody>
</table> This will look like:
Now, the same information is organized in rows and columns. Users can scan by column (e.g., find all employees from India) or by row (see all details of one person) in an instant.
Defining the Data Table
A well-structured table is more than just rows and cells. For screen readers and other assistive technologies, the right markup tells the story:
The data table’s semantic structure is already explained in the Tables Tutorial | Web Accessibility Initiative (WAI) | W3C. but just to be on the same page, here’s the basic concept of defining a data table:
<table>defines the table<tr>defines each row.<td>holds the data.<th>indicates headers. Column headers are defined together as a row of<th>elements, while row headers are defined as a single<th>within each row of<td>.
This semantic structure allows assistive technologies to announce relationships like: “Date of Birth, 01-01-2000.” Instead of just reading “01-01-2000” without context.
Labeling the Table
When there’s only one table on a page, users usually know what it represents. But if you have multiple tables, things get tricky.
For example, screen reader might announce both tables simply as: “Table with 3 rows and 5 columns.”
JAWS speech viewer screenshot:
NVDA speech viewer screenshot:
Confusing, right?
That’s why naming tables is important. You can do this using the <caption> element:
<table>
<caption>2025 Employees Details</caption>
...
</table>
<table>
<caption>2024 Employees Details</caption>
...
</table> JAWS announcement:
- “table with 5 columns and 3 rows 2025 Employees Details, Column 1, Row 1” for the first table.
- “table with 5 columns and 3 rows 2024 Employees Details, Column 1, Row 1” for the second table.
NVDA announcement:
- “2025 Employees Details table with 3 rows and 5 columns” for the first table.
- “2024 Employees Details table with 3 rows and 5 columns” for the second table.
Problem solved! Users now know exactly what each table represents.
Alternatively, aria-label or aria-labelledby can also be used to define the label for the table.
<table aria-label=”2024 Employees Details”>
...
</table> If a table is complex or requires extra explanation, you can add an aria-describedby attribute to provide a short description. This helps screen reader users understand the table’s purpose and structure before navigating through its data.
<p id="tableDesc">
This table provides employee details, including department, role, and seminar participation status for 2024.
</p>
<table aria-describedby="tableDesc">
...
</table> JAWS announcement:
“Table with 4 columns and 2 rows. This table provides employee details, including department, role, and seminar participation status for 2024. Column l, Row 1 Employee Name”
NVDA announcement:
“Table with 2 rows and 4 columns This table provides employee details, including department, role, and seminar participation status for 2024.”
summary property served a similar purpose in earlier HTML versions, but it is now deprecated in HTML5. While some assistive technologies may still recognize it, it’s best practice to use aria-describedby for long-term compatibility. Layout table
A layout table is a table used purely for visual arrangement of content rather than for presenting data. In layout tables, there is no meaningful relationship between cells, and the table is only used for decorative or structural purposes.
Layout tables are acceptable in WCAG and can be used to arrange page elements such as headers, sidebars, and footers. However, this approach works visually, but it introduces accessibility challenges. Screen readers and other assistive technologies interpret tables semantically — announcing rows, columns, and headers — which can confuse users when no real data relationship exists.
For this reason, table markup should not be used for page design or visual layout.
What Not to Use in Layout Tables
Assistive technologies depend on table elements like <th> and <caption> to announce structure and meaning. Including these in layout tables misleads users by suggesting relationships that don’t actually exist.
For layout tables, avoid:
headersattributescopeattributesummaryattribute<th>element<caption>element
Although WCAG does not prohibit the use of layout tables, CSS-based layouts are strongly recommended to preserve the proper semantic meaning of HTML table elements. When a table is used purely for layout, the <th> element, headers attribute, and scope attribute should not be used, since there is no actual relationship between data cells.
Likewise, the <caption> element and summary attribute should not be used, as they are intended to provide descriptions for data tables, not layout structures.
<table>
<tbody>
<tr>
<td colspan="2" >
<h1>Header Area</h1>
</td>
</tr>
<tr>
<td>
<p>Sidebar content goes here. <br>Links or navigation. </p>
</td>
<td>
<p>Main page content goes here. <br>This is where the main article, text, or image would be placed. </p>
</td>
</tr>
</tbody>
</table> When read by a screen reader, this table is treated as layout-only, and table structure is ignored.
Screen reader announces:
“heading level 1 Header Area. Sidebar content goes here. Links or navigation. Main page content goes here. This is where the main article, text, or image would be placed.”
JAWS speech screenshot:
NVDA speech screenshot:
Identifying Layout Tables
If your markup is long and complex, the easiest way to confirm whether a table is being treated as a layout table is to inspect the accessibility tree. In such cases, you’ll see the table identified as a “LayoutTable” rather than a semantic data table.
Making Layout Tables Invisible to Assistive Tech
Alternatively, role="presentation" or role="none" can be applied to remove the semantic meaning of a table from the accessibility API, even if elements like <table> or <th> are present.
<table role=”presentation”>
...
</table> With this approach, the entire table structure is ignored in the accessibility tree, and only the actual content inside the table is exposed to assistive technologies.
Making Tables Responsive While Preserving Accessibility
Tables often contain large amounts of data, which can make them difficult to view on smaller screens. While many developers try to make tables “fit” by hiding columns or reflowing them into lists, these approaches can break relationships between data cells and headers.
A simple and effective way to handle this is to make the table scrollable horizontally.
<div style="overflow-x: auto;">
<table>
...
</table>
</div> The overflow-x: auto; property allows users to scroll horizontally when the table width exceeds the viewport.
Conclusion
In my opinion, tables are one of the most powerful semantic tools in HTML, but they’re also among the most misunderstood. While lists and other tags can handle simple cases, tables shine when it comes to organizing and presenting complex datasets. A well-structured table does more than group information—it creates meaningful relationships between data and headers, making content more accessible for everyone, especially users of assistive technologies.
At the same time, tables should never be misused for layout purposes. Layout tables may still “work” visually, but can introduce barriers when screen readers announce them as data. By choosing semantic HTML elements appropriately and relying on CSS for layout, developers can create content that is both visually appealing and accessible to all users.
Resources
- Tables Tutorial | Web Accessibility Initiative (WAI) | W3C
- Table Pattern | APG | WAI | W3C
- Understanding Success Criterion 1.3.1: Info and Relationships | WAI | W3C
- Understanding Success Criterion 1.3.2: Meaningful Sequence | WAI | W3C
- F46: Failure of Success Criterion 1.3.1 due to using th elements, caption elements, or non-empty summary attributes in layout tables | WAI | W3C
- H39: Using caption elements to associate data table captions with data tables | WAI | W3C
Comments
Nice article, but I am missing a section about filtering and sorting.
That would make the article complete.