Skip to content

Tables Project

DodaTech 3 min read

title: "Tables Project — Build Complete Accessible Data Tables" description: "Build production-ready accessible data tables with proper structure, sorting, responsive patterns, and screen reader navigation support." weight: 12 date: 2026-06-28 lastmod: 2026-06-28 tags: [accessibility, tables]


Apply everything you learned by building a production-ready data table with headers, caption, sorting, and responsive behavior.

## What You'll Learn

How to combine all table patterns into a complete, accessible data table component.

## Why It Matters

A complete accessible table handles headers, captions, sorting, and responsive design together. Each pattern reinforces the others.

## Real-World Use

Doda Browser's download manager shows a sortable table with file name, size, status, and progress columns. The table has proper headers, a caption, and is scrollable on mobile.

## Complete Table

```html
<div class="table-container">
  <table aria-describedby="table-summary">
    <caption>Employee performance review scores</caption>
    <thead>
      <tr>
        <th scope="col" aria-sort="none">
          <button onclick="sortTable(0)">Employee</button>
        </th>
        <th scope="col" aria-sort="none">
          <button onclick="sortTable(1)">Department</button>
        </th>
        <th scope="col" aria-sort="descending">
          <button onclick="sortTable(2)">Score</button>
        </th>
        <th scope="col" aria-sort="none">
          <button onclick="sortTable(3)">Rating</button>
        </th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th scope="row">Alice Chen</th>
        <td>Engineering</td>
        <td>92</td>
        <td>Excellent</td>
      </tr>
      <tr>
        <th scope="row">Bob Smith</th>
        <td>Marketing</td>
        <td>78</td>
        <td>Good</td>
      </tr>
      <tr>
        <th scope="row">Carol Davis</th>
        <td>Sales</td>
        <td>85</td>
        <td>Very Good</td>
      </tr>
    </tbody>
  </table>
</div>

<p id="table-summary" class="sr-only">
  This table shows employee performance review scores sorted by score descending.
  Columns: employee name, department, score, and rating.
</p>

CSS

.table-container {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  max-width: 100%;
}

.table-container table {
  width: 100%;
  min-width: 500px;
  border-collapse: collapse;
}

.table-container th {
  background: #f5f5f5;
  padding: 0.75rem;
  text-align: left;
  position: relative;
}

.table-container th[aria-sort] button {
  background: none;
  border: none;
  font: inherit;
  cursor: pointer;
  padding: 0;
  color: inherit;
}

[aria-sort="ascending"]::after {
  content: " ▲";
}

[aria-sort="descending"]::after {
  content: " ▼";
}

JavaScript

function sortTable(columnIndex) {
  const table = document.querySelector('table');
  const headers = table.querySelectorAll('th[aria-sort]');
  const tbody = table.querySelector('tbody');
  const rows = Array.from(tbody.querySelectorAll('tr'));

  const clickedHeader = headers[columnIndex];
  const currentSort = clickedHeader.getAttribute('aria-sort');
  const newDirection = currentSort === 'ascending' ? 'descending' :
                      currentSort === 'descending' ? 'ascending' : 'ascending';

  headers.forEach(h => h.setAttribute('aria-sort', 'none'));
  clickedHeader.setAttribute('aria-sort', newDirection);

  rows.sort((a, b) => {
    const aVal = a.children[columnIndex].textContent.trim();
    const bVal = b.children[columnIndex].textContent.trim();
    const numA = parseFloat(aVal);
    const numB = parseFloat(bVal);
    const compare = isNaN(numA) || isNaN(numB)
      ? aVal.localeCompare(bVal)
      : numA - numB;
    return newDirection === 'ascending' ? compare : -compare;
  });

  rows.forEach(row => tbody.appendChild(row));
}

Common Mistakes

1. Missing responsive container

Without overflow-x: auto, the table breaks the layout on mobile.

2. Sort buttons not accessible

Buttons inside th must be focusable, keyboard-operable, and have visible focus indicators.

3. No summary for complex tables

Complex tables need a summary description explaining the structure.

4. Inconsistent sort indicators

Visual sort indicators must match aria-sort values. Arrow direction must be consistent.

5. No row hover or stripe styles

Visual striping or hover helps all users track rows across columns.

Practice Questions

1. What is the minimum data table needs for accessibility? Caption, th with scope, thead/tbody, and proper associations for complex cells.

2. How do sortable headers communicate state to screen readers? Via aria-sort on the th element (ascending, descending, none).

3. What responsive pattern preserves table accessibility on mobile? Horizontal scroll wrapper with min-width and sticky first column.

Challenge: Extend the table with row hover highlighting, zebra striping, and a sticky first column for mobile scrolling.

FAQ

Should I use a framework for sortable tables?

Frameworks like DataTables can be made accessible, but custom implementations give you full control over ARIA attributes.

How do I handle very large datasets?

Use server-side pagination with proper aria-sort and focus management. Do not render 10,000 rows in the DOM.

Can I use virtual scrolling?

Yes, but ensure proper table semantics are maintained in the virtual viewport.

How do I add row selection?

Use checkboxes in the first column with proper labels and aria-selected attributes.

What about inline editing?

Inline editing in tables is complex. Each editable cell needs proper form controls with labels.

Mini Project

Build a complete data table for an admin dashboard showing user accounts. Include sortable columns (name, email, role, status, last login), responsive scrolling, and proper ARIA.

What's Next

Now explore Accessible Images to learn how to make images and graphics accessible.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro