Responsive stacked table
Category: Tables & lists · Tags: table, responsive, mobile, reflow, cards
| 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.