Skip to main content

Accessible Design System v1

Type to filter components by name, description, or keywords

Accessible Tables

Accessible data tables with proper semantic markup, headers, captions, and scope attributes to ensure screen reader users can understand table relationships and navigate efficiently.

Key Accessibility Features

  • Caption Element: Describes the table's purpose for all users
  • Table Headers: Uses <th> elements with scope attributes
  • Semantic Structure: Proper <thead>, <tbody>, and <tfoot> elements
  • Scope Attributes: Clarifies header relationships (col, row, colgroup, rowgroup)
  • Responsive Design: Horizontal scroll on small screens to preserve table structure

Basic Data Table

Simple table with column headers and a caption:

Employee Directory
Name Department Email Phone
Jane Smith Engineering jane.smith@example.com 555-0101
John Doe Design john.doe@example.com 555-0102
Maria Garcia Marketing maria.garcia@example.com 555-0103

HTML

<table class="ads-table">
    <caption>Employee Directory</caption>
    <thead>
        <tr>
            <th scope="col">Name</th>
            <th scope="col">Department</th>
            <th scope="col">Email</th>
            <th scope="col">Phone</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Jane Smith</td>
            <td>Engineering</td>
            <td>jane.smith@example.com</td>
            <td>555-0101</td>
        </tr>
        <tr>
            <td>John Doe</td>
            <td>Design</td>
            <td>john.doe@example.com</td>
            <td>555-0102</td>
        </tr>
    </tbody>
</table>

CSS

.ads-table {
    width: 100%;
    border-collapse: collapse;
    background: white;
}

.ads-table caption {
    text-align: left;
    font-weight: 500;
    padding: 1rem;
    background: var(--ads-bg-secondary);
    border: 1px solid var(--ads-border-color);
    border-bottom: none;
}

.ads-table thead {
    background: var(--ads-bg-secondary);
}

.ads-table th {
    padding: 0.75rem;
    text-align: left;
    border: 1px solid var(--ads-border-color);
    font-weight: 600;
}

.ads-table td {
    padding: 0.75rem;
    border: 1px solid var(--ads-border-color);
}

.ads-table tbody tr:nth-child(even) {
    background: var(--ads-bg-secondary);
}

/* Responsive wrapper */
.ads-table-wrapper {
    overflow-x: auto;
}

@media (max-width: 768px) {
    .ads-table {
        font-size: 0.875rem;
    }
    
    .ads-table th,
    .ads-table td {
        padding: 0.5rem;
    }
}

Table with Row Headers

When the first column contains header information for each row, use scope="row":

Q1 Sales by Region
Region January February March
North $125,000 $138,000 $142,000
South $98,000 $105,000 $112,000
East $156,000 $162,000 $178,000
Total $379,000 $405,000 $432,000

HTML

<table class="ads-table">
    <caption>Q1 Sales by Region</caption>
    <thead>
        <tr>
            <th scope="col">Region</th>
            <th scope="col">January</th>
            <th scope="col">February</th>
            <th scope="col">March</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <th scope="row">North</th>
            <td>$125,000</td>
            <td>$138,000</td>
            <td>$142,000</td>
        </tr>
        <tr>
            <th scope="row">South</th>
            <td>$98,000</td>
            <td>$105,000</td>
            <td>$112,000</td>
        </tr>
    </tbody>
    <tfoot>
        <tr>
            <th scope="row">Total</th>
            <td>$379,000</td>
            <td>$405,000</td>
            <td>$432,000</td>
        </tr>
    </tfoot>
</table>

Best Practices

  • Always include a <caption> element to describe the table's purpose
  • Use <th> elements for all header cells, not <td>
  • Add scope="col" for column headers and scope="row" for row headers
  • Use <thead>, <tbody>, and <tfoot> to group related rows
  • Provide adequate color contrast (4.5:1 minimum) for all text
  • Don't use tables for layout - only for tabular data
  • On mobile, wrap tables in a scrollable container rather than hiding columns
  • For complex tables, consider using headers attribute with id values