⚡IntermediateAdvanced Tables

Responsive Table Patterns

Make data tables work on mobile with horizontal scroll, stacked layout, and card transforms.

Overview

HTML tables are rigid by nature they must maintain column alignment. On small screens, wide tables overflow horizontally and become unreadable or break the layout. Three common responsive table patterns: 1. Horizontal scroll: the simplest wrap table in a scrollable container 2. Stacked layout: transform table rows into cards with CSS, using data-label attributes 3. Priority columns: hide less important columns on small screens Each pattern trades some accessibility or information for mobile usability. The stacked approach is most mobile-friendly but requires the most CSS. Horizontal scroll preserves full data and is most accessible.

Key Points

  • •Horizontal scroll wrapper: overflow-x: auto on a containing div
  • •Stacked: display: block on table elements; data-label attribute for cell labels
  • •Priority pattern: responsive classes hide columns at breakpoints
  • •Horizontal scroll is safest for accessibility table semantics are preserved
  • •Stacked layout breaks screen reader table navigation use with caution
  • •caption/aria-label on the table helps users understand what they're navigating

Examples

Pattern 1: Horizontal scroll (simplest)

<style>
  .table-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;  /* smooth iOS scroll */
  }
  table { min-width: 600px; width: 100%; border-collapse: collapse; }
</style>

<div class="table-scroll" role="region" aria-label="Sales data scroll horizontally">
  <table>
    <caption>Monthly Sales Report</caption>
    <thead>
      <tr>
        <th scope="col">Product</th>
        <th scope="col">Jan</th>
        <th scope="col">Feb</th>
        <th scope="col">Mar</th>
        <th scope="col">Total</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Widget A</td>
        <td>120</td>
        <td>145</td>
        <td>132</td>
        <td>397</td>
      </tr>
    </tbody>
  </table>
</div>

role="region" + aria-label on the scroll container announces the scrollable area to screen readers.

Pattern 2: Stacked card layout

<style>
  @media (max-width: 640px) {
    table, thead, tbody, th, td, tr { display: block; }
    thead tr { position: absolute; top: -9999px; left: -9999px; } /* hide headers */
    tr { border: 1px solid #ccc; margin-bottom: 12px; border-radius: 8px; }
    td {
      display: flex;
      justify-content: space-between;
      padding: 8px 12px;
      border-bottom: 1px solid #eee;
    }
    /* Show column label before each value */
    td::before {
      content: attr(data-label);
      font-weight: bold;
      color: #374151;
    }
  }
</style>

<table>
  <thead>
    <tr>
      <th scope="col">Name</th>
      <th scope="col">Role</th>
      <th scope="col">Salary</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td data-label="Name">Alice Smith</td>
      <td data-label="Role">Developer</td>
      <td data-label="Salary">$95,000</td>
    </tr>
  </tbody>
</table>

data-label on each td mirrors the column header used by CSS ::before for mobile labels.

Pattern 3: Priority columns

<style>
  @media (max-width: 480px) {
    .col-secondary { display: none; }
  }
</style>

<table>
  <thead>
    <tr>
      <th scope="col">Name</th>               <!-- always visible -->
      <th scope="col" class="col-secondary">Email</th>    <!-- hidden on mobile -->
      <th scope="col" class="col-secondary">Joined</th>   <!-- hidden on mobile -->
      <th scope="col">Status</th>             <!-- always visible -->
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Alice</td>
      <td class="col-secondary">alice@example.com</td>
      <td class="col-secondary">2023-01-15</td>
      <td>Active</td>
    </tr>
  </tbody>
</table>

Common Mistakes

  • Using the stacked pattern for complex tables with merged cells colspan/rowspan breaks
  • Not adding role="region" + aria-label on the horizontal scroll wrapper screen readers can't tell it's scrollable
  • Hiding column headers off-screen with visibility:hidden in stacked layout use absolute positioning trick instead

Note

For most tables, horizontal scroll is the best default. It's accessible, simple, and preserves data. Use the stacked pattern only for simple flat tables with no merged cells.

Exercises

Write HTML to solve each exercise. Use Preview to see it render in a live iframe, then mark as done.

Loading editor…

Completed: 0 / 5 exercises

Knowledge Check

Question 1 of 4

Which responsive table pattern is most accessible?