Data tables are everywhere on the web. Pricing comparisons, schedules, financial summaries, product specifications, sports standings, and research data are all commonly presented in tables. When built well, a table organizes complex information so it is easy to scan and understand. When built poorly, it becomes a frustrating barrier for users who rely on screen readers, keyboard navigation, or other assistive technology.

The problem is that many developers treat tables as a layout tool rather than a semantic structure. They use tables to position content on the page, or they build data tables without the proper markup that assistive technology needs to understand them. The result is a jumbled, confusing experience for users who cannot see the visual layout.

In this guide, we will walk through what makes a data table accessible, what WCAG requires, how screen readers interact with tables, and how to build both simple and complex tables that work for everyone.

Why Table Accessibility Matters

Imagine you are using a screen reader and you encounter a table. The screen reader announces the contents of each cell one at a time, moving left to right and top to bottom. Without context, you hear numbers and words with no way to know which column or row they belong to. Is “42” the price, the quantity, or the product ID? Is “Monday” a row header or a data point?

Sighted users can glance at a table and instantly understand the relationship between rows, columns, and cells because they can see the visual alignment and layout. Screen reader users do not have that visual context. They depend on proper HTML markup that explicitly defines the relationships between cells and their headers.

When a table is built accessibly, the screen reader can announce the row header and column header along with each cell value. Instead of hearing “42” alone, the user hears something like “Price, Widget A, 42 dollars.” That context transforms the experience from confusing to meaningful.

Keyboard users also benefit from accessible tables. Properly structured tables with semantic HTML are easier to navigate using keyboard commands. Screen reader software provides table navigation shortcuts, but they only work when the table is marked up correctly.

WCAG Requirements for Tables

Several WCAG success criteria apply directly to data tables.

1.3.1 Info and Relationships (Level A)

This is the most important success criterion for tables. It requires that information and relationships that are conveyed visually must also be conveyed programmatically. In a table, the visual relationship between a header cell and the data cells it describes must be available in the code. This means using the correct HTML elements: <th> for headers, <td> for data cells, and the scope attribute or headers attribute to define the relationship.

1.3.2 Meaningful Sequence (Level A)

The order in which content is presented must be meaningful. For tables, this means the HTML should reflect the logical reading order of the data. A screen reader reads the table in the order the code presents it, so the code must follow a logical sequence.

2.4.6 Headings and Labels (Level AA)

This success criterion requires that headings and labels are descriptive. For tables, the <caption> element and header cells should clearly describe what the table contains and what each row or column represents.

4.1.2 Name, Role, Value (Level A)

For user interface components, this criterion requires that the name, role, and value of components can be determined programmatically. While this applies more broadly than tables, it reinforces the need for proper semantic markup.

For a broader overview of WCAG requirements, see our guide on the levels of WCAG compliance.

The Right HTML Elements for Accessible Tables

Building an accessible table starts with using the right HTML elements for the right purposes. Let us walk through each one.

The Table Element

The <table> element wraps the entire table. It tells assistive technology that what follows is tabular data, not free flowing content. This is important because screen readers switch into table navigation mode when they encounter a <table> element, giving the user special commands for moving between cells, rows, and columns.

Caption

The <caption> element provides a title or summary for the table. It should be the first child inside the <table> element. A good caption helps all users understand the purpose of the table before diving into the data. It is especially valuable for screen reader users who want to decide whether a table is worth exploring before navigating through it cell by cell.

<table>
  <caption>Product Pricing Comparison, January 2026</caption>
  ...
</table>

Table Head, Body, and Foot

Use <thead>, <tbody>, and <tfoot> to group rows logically. The <thead> element contains the header row, <tbody> contains the main data, and <tfoot> contains summary rows like totals. Screen readers use these sections to help users understand the structure of the table.

Header Cells

The <th> element marks a cell as a header. This is the single most important element for table accessibility. When a cell is marked as a <th>, screen readers know to announce it as a header when reading related data cells.

Header cells can be row headers or column headers. You tell the browser and assistive technology which direction a header applies using the scope attribute.

The Scope Attribute

The scope attribute tells the browser whether a header applies to a row or a column. It takes two primary values:

  • scope="col" means the header applies to all cells below it in the same column.
  • scope="row" means the header applies to all cells to the right of it in the same row.
<table>
  <caption>Monthly Revenue</caption>
  <thead>
    <tr>
      <th scope="col">Month</th>
      <th scope="col">Revenue</th>
      <th scope="col">Expenses</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">January</th>
      <td>$10,000</td>
      <td>$6,500</td>
    </tr>
    <tr>
      <th scope="row">February</th>
      <td>$12,000</td>
      <td>$7,000</td>
    </tr>
  </tbody>
</table>

In this example, the screen reader will announce “January, Revenue, $10,000” when the user reaches that cell, giving the user full context. Without the scope attribute, the screen reader might only announce “$10,000” with no indication of what the number represents.

Data Cells

The <td> element represents a data cell. Every cell that contains data rather than a header should use <td>. This distinction matters because it tells the screen reader which cells are headers and which are values.

Building a Simple Accessible Table

Let us put it all together with a complete example. Here is a properly built simple table:

<table>
  <caption>Plan Comparison, March 2026</caption>
  <thead>
    <tr>
      <th scope="col">Plan</th>
      <th scope="col">Price per Month</th>
      <th scope="col">Pages Included</th>
      <th scope="col">Monthly Scans</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Starter</th>
      <td>$49</td>
      <td>5</td>
      <td>10</td>
    </tr>
    <tr>
      <th scope="row">Professional</th>
      <td>$149</td>
      <td>25</td>
      <td>50</td>
    </tr>
    <tr>
      <th scope="row">Enterprise</th>
      <td>$399</td>
      <td>Unlimited</td>
      <td>Unlimited</td>
    </tr>
  </tbody>
</table>

This table is accessible because it has a descriptive caption, clear column headers with scope="col", row headers with scope="row", and proper use of <thead> and <tbody>. A screen reader user can navigate this table and always know which plan, which attribute, and which value they are hearing.

Common Mistakes That Break Table Accessibility

Even well intentioned developers make mistakes with tables. Here are the most common ones and how to avoid them.

Using Tables for Layout

This is the most common and most damaging mistake. In the early days of the web, tables were used to create page layouts because CSS support was limited. That practice should be completely abandoned. Using a table for layout confuses screen readers because they switch into table navigation mode when they encounter a <table> element, presenting the layout content as if it were tabular data.

If you need to lay out content in columns or a grid, use CSS Grid or Flexbox. Save the <table> element for actual tabular data where rows and columns have a meaningful relationship.

Missing Header Cells

Every table should have header cells that identify the data in each row and column. A table with no <th> elements forces screen reader users to guess the meaning of each cell based on position alone, which is unreliable and frustrating.

Missing Scope Attributes

Even when developers use <th> elements, they often forget the scope attribute. Without it, the screen reader has to guess whether the header applies to a row or a column. While some screen readers make reasonable guesses, you should never rely on that. Always include scope="col" or scope="row" on every <th>.

Missing Captions

Without a caption, screen reader users have to navigate into the table to find out what it contains. A caption gives them context upfront so they can decide whether the table is relevant to them. Every data table should have a <caption>.

Using Deprecated Attributes for Styling

Attributes like border, cellpadding, cellspacing, width, align, and valign are deprecated and should not be used. Styling belongs in CSS. While these attributes do not directly break accessibility, they contribute to messy, unmaintainable code and can cause inconsistent rendering across devices.

Empty Cells for Layout

Sometimes developers leave empty cells in a table to create visual spacing. This is confusing for screen reader users who encounter an empty cell and do not know whether it means “no data” or “spacing.” If a cell genuinely has no data, consider using a visually hidden text alternative like “Not applicable” or “None” so the screen reader announces something meaningful.

Complex Tables: When Scope Is Not Enough

Simple tables have one row of column headers and one column of row headers. Complex tables have multiple levels of headers, merged cells, or headers that span multiple rows or columns. These tables are harder to make accessible and require a different approach.

The Headers Attribute

For complex tables, the scope attribute is not enough because a single data cell might be described by multiple headers at different levels. In these cases, use the headers attribute on <td> elements to explicitly list which header cells describe that data cell.

Each header cell needs a unique id attribute, and each data cell lists the id values of its headers in the headers attribute:

<table>
  <caption>Quarterly Sales by Region and Product</caption>
  <thead>
    <tr>
      <th id="region" scope="col">Region</th>
      <th id="product" scope="col">Product</th>
      <th id="q1" scope="col">Q1 Sales</th>
      <th id="q2" scope="col">Q2 Sales</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th id="north" scope="row">North</th>
      <th id="widget-n" scope="row">Widget A</th>
      <td headers="region product q1 north widget-n">$5,000</td>
      <td headers="region product q2 north widget-n">$5,500</td>
    </tr>
    <tr>
      <th id="south" scope="row">South</th>
      <th id="widget-s" scope="row">Widget A</th>
      <td headers="region product q1 south widget-s">$3,200</td>
      <td headers="region product q2 south widget-s">$3,800</td>
    </tr>
  </tbody>
</table>

In this example, each data cell explicitly references all the headers that describe it. The screen reader will announce all relevant headers before the cell value, giving the user complete context even in a complex multi level table.

Merged Cells: Colspan and Rowspan

The colspan and rowspan attributes allow a cell to span multiple columns or rows. While these are valid HTML, they can make tables harder for screen reader users to navigate because the logical grid is broken by merged cells.

If you must use merged cells, make sure your header relationships are crystal clear. Use the headers attribute on every data cell so the screen reader knows exactly which headers apply, regardless of how cells are merged. Test the table with a real screen reader to make sure the reading order makes sense.

When to Split Complex Tables

If a table is extremely complex, consider whether it could be split into two or more simpler tables. Multiple simple tables are often easier for all users to understand than one very complex table with merged cells and multiple header levels. Ask yourself whether the complexity is truly necessary or whether simpler tables would convey the same information more clearly.

Responsive Tables: Making Them Work on Mobile

Tables are inherently difficult on small screens. A table with six columns that looks fine on a desktop monitor can overflow horizontally on a phone, forcing users to scroll sideways or zoom out until the text is unreadable.

Here are some strategies for making tables work on mobile devices.

Let the Table Scroll Horizontally

The simplest approach is to wrap the table in a container that scrolls horizontally on small screens. This preserves the table structure while allowing users to see all the data:

.table-wrapper {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

This is the most accessible option because it preserves the table semantics and the header relationships. Screen readers still understand the table structure, and sighted users can scroll to see all columns.

Reflow to a Stacked Layout

For simpler tables, you can use CSS to reflow the table into a stacked layout on small screens, where each row becomes a card and the headers are repeated for each data point. This approach uses data-label attributes or pseudo elements to display the header alongside each value. However, this breaks the native table semantics, so use it carefully and test with screen readers.

Hide Non Essential Columns

On small screens, you can hide less important columns using CSS. For example, in a pricing table, you might show the plan name and price but hide the “features” column on mobile, providing that information through a separate link instead. Be cautious with this approach, as hiding content can remove information that some users need.

Test on Real Devices

Whatever responsive approach you choose, test it on actual mobile devices with real screen readers. CSS solutions that look good visually can sometimes break the experience for assistive technology. See our guide on mobile web accessibility for more on designing for every screen.

Tables and Screen Reader Navigation

Different screen readers handle tables in different ways, but most provide navigation commands that let users move through a table cell by cell, row by row, or column by column. Here is what you need to know.

NVDA

NVDA is a free, open source screen reader for Windows. When a user encounters a table, NVDA announces “Table with X rows and Y columns.” The user can navigate using arrow keys to move between cells. NVDA announces the column header and row header along with the cell content when the table is properly marked up.

JAWS

JAWS is a widely used commercial screen reader. It provides table navigation commands like Ctrl+Alt+Arrow keys to move between cells. JAWS also supports a feature that lets users list all tables on a page and jump directly to one, which is why descriptive captions are so important.

VoiceOver

VoiceOver is the screen reader built into Apple devices. It announces tables and provides gestures for navigating between rows and columns. On iOS, users can swipe up or down to move through the table. VoiceOver reads the header information for each cell when properly marked up.

TalkBack

TalkBack is the screen reader built into Android devices. It provides similar table navigation to VoiceOver, with gestures for moving between cells. TalkBack announces header information when the table uses proper semantic markup.

The key takeaway is that all major screen readers rely on the same HTML structure: <table>, <th>, scope, headers, and <caption>. If you build your table with these elements correctly, it will work across all screen readers. For more on how screen readers interact with web content, see our guide on screen reader accessibility.

Testing Your Tables for Accessibility

Building an accessible table is only half the work. You also need to test it. Here are the key testing steps.

Automated Scanning

Run an automated accessibility scanner on your page. Tools like Accessible Metrics can detect many table related issues, including missing <th> elements, missing scope attributes, and tables used for layout. However, automated tools cannot catch every issue. For example, they cannot tell whether your caption is descriptive or whether a complex table’s headers attributes are correctly mapping the right relationships.

Keyboard Testing

Navigate your table using only a keyboard. You should be able to move through the table logically using Tab and arrow keys. If focus gets trapped or skips cells, there is a problem. See our guide on keyboard accessibility for more on keyboard testing.

Screen Reader Testing

Test your table with at least one screen reader. NVDA is free and works on Windows. VoiceOver is built into macOS and iOS. Listen to how the table is announced. Does the screen reader tell you how many rows and columns there are? Does it announce the headers along with each cell? Can you navigate using table commands? If the answers are no, your table needs more work.

Visual Inspection

Make sure the table is visually clear for sighted users. Are the headers visually distinguishable from data cells? Is there enough spacing between cells? Does the text contrast meet WCAG standards? For more on contrast requirements, see our guide on color blindness and web accessibility.

Test on Mobile

Check your table on a phone and tablet. Does it scroll or reflow properly? Is the text readable? Does the screen reader still announce the table correctly on mobile? For more on testing approaches, see our article on how to test the accessibility of your website.

A Quick Checklist for Accessible Tables

Use this checklist when building or reviewing data tables:

  • Use the <table> element only for tabular data, never for layout.
  • Include a <caption> element that describes the table.
  • Use <thead>, <tbody>, and <tfoot> to structure the table.
  • Mark header cells with <th>.
  • Add scope="col" or scope="row" to every header cell.
  • Use the headers attribute for complex tables with multiple levels of headers.
  • Give each header cell a unique id when using the headers attribute.
  • Avoid empty cells or provide meaningful text for them.
  • Do not use deprecated styling attributes like border or cellpadding.
  • Make the table responsive for mobile screens.
  • Test with a keyboard to verify logical navigation.
  • Test with a screen reader to confirm headers are announced correctly.
  • Ensure text within the table meets WCAG contrast requirements.
  • Test on real mobile devices with screen readers.

The Bigger Picture: Tables as Part of an Accessible Site

Accessible tables are just one piece of a fully accessible website. Your forms, navigation, images, color choices, and content structure all need to meet accessibility standards. Building an accessible table means nothing if your form fields have no labels or your images have no alt text. Accessibility is a holistic practice that touches every part of your site.

For a comprehensive guide to all the areas you need to cover, see our complete ADA compliance website checklist and our WCAG checklist for business and government.

Conclusion

Data tables are a powerful way to present complex information, but they require careful markup to be accessible. The good news is that the rules are straightforward: use semantic HTML, include captions, mark your headers, define their scope, and use the headers attribute for complex tables. Test with keyboards and screen readers to make sure real users can navigate your tables successfully.

When you take the time to build accessible tables, you ensure that all your users can access your data, regardless of how they interact with your website. That is what accessibility is all about.


Want to make sure your tables and every other element on your site meet accessibility standards? Sign up for Accessible Metrics and run a free accessibility scan today. You will get a detailed report against WCAG 2.1 AA standards that shows exactly what needs fixing and gives you a clear path to making your website accessible to everyone.