Table
Data table with caption and scope attributes, and an accessible scroll region for wide content.
- Use native
<table class="table">inside a.table-wrapfor surface, border, radius, and overflow. - Use
<caption>,scope="col", andscope="row"for accessibility. - Use
.text-endfor numeric columns and.text-nowrapfor non-wrapping cells. - For wide tables, make the wrapper an accessible scroll region with
role="region",aria-labelledby, andtabindex="0". - Tables stay content-first and neutral by default.
.tableis a static, content-first table. It provides no sorting, filtering, search, selection, pagination, or row actions. For an interactive data grid ("datatable"), use a dedicated component — see the data grid template, which themesdata-grid-componentwith Actual CSS tokens..tabledoes not consume shared variants or intent colors (--ui-bg/--intent): a.soft+ intent combo on a<tr>has no effect. To tint rows or headers, style them with a class of your own.
Class reference
| Class | Kind | Description |
|---|---|---|
.table-wrap | Component | Scroll container and outer chrome (surface, border, radius). |
.table | Component | The table itself; row and cell rules. |
.table.compact | Density | Tighter cell padding; font size and icons unchanged. |
Basic usage
| Name | Role | Status | |
|---|---|---|---|
| Alice Johnson | alice@example.com | Admin | Active |
| Bob Smith | bob@example.com | Editor | Active |
| Carol White | carol@example.com | Viewer | Pending |
<div class="table-wrap">
<table class="table">
<caption>Team members</caption>
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Email</th>
<th scope="col">Role</th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>Alice Johnson</td>
<td>alice@example.com</td>
<td>Admin</td>
<td><span class="badge success soft">Active</span></td>
</tr>
<tr>
<td>Bob Smith</td>
<td>bob@example.com</td>
<td>Editor</td>
<td><span class="badge success soft">Active</span></td>
</tr>
<tr>
<td>Carol White</td>
<td>carol@example.com</td>
<td>Viewer</td>
<td><span class="badge secondary soft">Pending</span></td>
</tr>
</tbody>
</table>
</div>
Accessible scroll region
For wide tables, add role="region", aria-labelledby, and tabindex="0" to
the wrapper so keyboard users can scroll it. --table-min sets the minimum
table width before the wrapper scrolls horizontally.
| Month | Plan | Revenue | Churn |
|---|---|---|---|
| March 2026 | Team | $42,128 | 1.2% |
| April 2026 | Team + Business | $48,902 | 0.9% |
| May 2026 | Team + Business | $53,470 | 0.7% |
<div class="table-wrap" role="region" aria-labelledby="revenue-table" tabindex="0">
<table class="table" style="--table-min: 64rem">
<caption id="revenue-table">Revenue by month</caption>
<thead>
<tr>
<th scope="col">Month</th>
<th scope="col">Plan</th>
<th scope="col" class="text-end">Revenue</th>
<th scope="col" class="text-end">Churn</th>
</tr>
</thead>
<tbody>
<tr>
<td>March 2026</td>
<td>Team</td>
<td class="text-end text-nowrap">$42,128</td>
<td class="text-end text-nowrap">1.2%</td>
</tr>
<tr>
<td>April 2026</td>
<td>Team + Business</td>
<td class="text-end text-nowrap">$48,902</td>
<td class="text-end text-nowrap">0.9%</td>
</tr>
<tr>
<td>May 2026</td>
<td>Team + Business</td>
<td class="text-end text-nowrap">$53,470</td>
<td class="text-end text-nowrap">0.7%</td>
</tr>
</tbody>
</table>
</div>
Compact tables
.compact lowers cell padding and establishes compact density for controls or
spacing helpers inside cells. The text stays as set; a data-heavy table that
also wants condensed type sets font-stretch on itself. It is the same
vocabulary as .card.compact.
| Pos | Name |
|---|---|
| 1 | John Nemo |
| 2 | Jane Doe |
| 3 | Ada Morgan |
<div class="table-wrap">
<table class="table compact">
<caption>Standings by position</caption>
<thead>
<tr>
<th scope="col">Pos</th>
<th scope="col">Name</th>
</tr>
</thead>
<tbody>
<tr><td>1</td><td>John Nemo</td></tr>
<tr><td>2</td><td>Jane Doe</td></tr>
<tr><td>3</td><td>Ada Morgan</td></tr>
</tbody>
</table>
</div>
When a table stops reading as a table
Responsive strategy is horizontal overflow: the wrap scrolls, the table keeps
its columns and its column scanability. No data-label restructuring, no
second representation of the data.
For datasets that no longer read well as a table on narrow screens, compose a
different view instead of restructuring the table: a .list / .media row
with badges and actions for a CRUD collection, .data-list
for one object's properties, or a dedicated data grid for interactive tables
(sorting, filtering, selection). Two views for two contexts is a feature, not
a duplication.
CSS hooks
--table-cell-pad— cell padding;.compactre-declares it tighter.--table-min— minimum table width before.table-wrapscrolls horizontally.--table-header-bg,--table-header-fg— header band fill and ink. Set them on a theme root rather than on.table: a data grid bridge reads the same pair, so the table and the grid share one header (see the data grid template).