Component Library

← Back to gallery

Responsive stacked table

Category: Tables & lists · Tags: table, responsive, mobile, reflow, cards

Recent orders
Order Customer Date Total Status
#10482 Lena Ortiz 4 Mar $128.40 Shipped
#10481 Sam Okafor 3 Mar $42.00 Processing
#10479 Mei Tanaka 1 Mar $315.75 Delivered

How to use it

Use for a short table with a handful of columns that must stay readable on phones without sideways scrolling, such as recent orders. Do not use it for wide numeric tables people compare down a column (use a scrolling data table) or for more than about six columns. After copying, keep every cell's data-label identical to its column header text and update the caption.

Accessibility

A real <table> with a caption and scoped headers; under 640px wide each row reflows into a card and each cell shows its column name from data-label through CSS. Because changing a table's display can strip its semantics in some browsers, the rows and cells also carry explicit table roles (row, columnheader, rowheader, cell), so screen readers still announce headers in the card layout. Cell text is #0f172a on #ffffff (17.85:1), header text #0f172a on #f1f5f9 (16.30:1), and the status pill #1e3a8a on #dbeafe (8.49:1).

Source (copy-ready)

<div class="dcl-stacked-table">
  <table class="dcl-stacked-table__table" role="table">
    <caption class="dcl-stacked-table__caption">Recent orders</caption>
    <thead class="dcl-stacked-table__head" role="rowgroup">
      <tr role="row">
        <th scope="col" role="columnheader" class="dcl-stacked-table__th">Order</th>
        <th scope="col" role="columnheader" class="dcl-stacked-table__th">Customer</th>
        <th scope="col" role="columnheader" class="dcl-stacked-table__th">Date</th>
        <th scope="col" role="columnheader" class="dcl-stacked-table__th">Total</th>
        <th scope="col" role="columnheader" class="dcl-stacked-table__th">Status</th>
      </tr>
    </thead>
    <tbody role="rowgroup">
      <tr role="row" class="dcl-stacked-table__row">
        <th scope="row" role="rowheader" class="dcl-stacked-table__cell" data-label="Order">#10482</th>
        <td role="cell" class="dcl-stacked-table__cell" data-label="Customer">Lena Ortiz</td>
        <td role="cell" class="dcl-stacked-table__cell" data-label="Date">4 Mar</td>
        <td role="cell" class="dcl-stacked-table__cell" data-label="Total">$128.40</td>
        <td role="cell" class="dcl-stacked-table__cell" data-label="Status"><span class="dcl-stacked-table__pill">Shipped</span></td>
      </tr>
      <tr role="row" class="dcl-stacked-table__row">
        <th scope="row" role="rowheader" class="dcl-stacked-table__cell" data-label="Order">#10481</th>
        <td role="cell" class="dcl-stacked-table__cell" data-label="Customer">Sam Okafor</td>
        <td role="cell" class="dcl-stacked-table__cell" data-label="Date">3 Mar</td>
        <td role="cell" class="dcl-stacked-table__cell" data-label="Total">$42.00</td>
        <td role="cell" class="dcl-stacked-table__cell" data-label="Status"><span class="dcl-stacked-table__pill">Processing</span></td>
      </tr>
      <tr role="row" class="dcl-stacked-table__row">
        <th scope="row" role="rowheader" class="dcl-stacked-table__cell" data-label="Order">#10479</th>
        <td role="cell" class="dcl-stacked-table__cell" data-label="Customer">Mei Tanaka</td>
        <td role="cell" class="dcl-stacked-table__cell" data-label="Date">1 Mar</td>
        <td role="cell" class="dcl-stacked-table__cell" data-label="Total">$315.75</td>
        <td role="cell" class="dcl-stacked-table__cell" data-label="Status"><span class="dcl-stacked-table__pill">Delivered</span></td>
      </tr>
    </tbody>
  </table>
</div>
<style>
.dcl-stacked-table { max-width: 100%; font: inherit; color: #0f172a; background-color: #ffffff; }
.dcl-stacked-table__table { width: 100%; border-collapse: collapse; }
.dcl-stacked-table__caption { padding: 0.5rem 0; text-align: left; font-weight: 700; color: #0f172a; background-color: #ffffff; }
.dcl-stacked-table__th { padding: 0.6rem 0.75rem; text-align: left; border-bottom: 2px solid #cbd5e1; color: #0f172a; background-color: #f1f5f9; }
.dcl-stacked-table__cell { padding: 0.6rem 0.75rem; text-align: left; font-weight: 400; border-bottom: 1px solid #e2e8f0; overflow-wrap: anywhere; color: #0f172a; background-color: #ffffff; }
.dcl-stacked-table__pill { display: inline-block; padding: 0.1rem 0.55rem; border-radius: 999px; font-size: 0.85rem; font-weight: 600; color: #1e3a8a; background-color: #dbeafe; }
@media (max-width: 639px) {
  .dcl-stacked-table__table, .dcl-stacked-table__table tbody, .dcl-stacked-table__row, .dcl-stacked-table__cell { display: block; }
  .dcl-stacked-table__caption { display: block; }
  .dcl-stacked-table__head { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .dcl-stacked-table__row { margin-bottom: 0.75rem; border: 1px solid #cbd5e1; border-radius: 8px; overflow: hidden; }
  .dcl-stacked-table__cell { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.5rem; }
  .dcl-stacked-table__cell::before { content: attr(data-label); font-weight: 700; }
}
</style>

Was this useful? Send feedback.