Actual CSS

Table

Data table with caption and scope attributes, and an accessible scroll region for wide content.

  • Use native <table class="table"> inside a .table-wrap for surface, border, radius, and overflow.
  • Use <caption>, scope="col", and scope="row" for accessibility.
  • Use .text-end for numeric columns and .text-nowrap for non-wrapping cells.
  • For wide tables, make the wrapper an accessible scroll region with role="region", aria-labelledby, and tabindex="0".
  • Tables stay content-first and neutral by default.
  • .table is 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 themes data-grid-component with Actual CSS tokens.
  • .table does 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

ClassKindDescription
.table-wrapComponentScroll container and outer chrome (surface, border, radius).
.tableComponentThe table itself; row and cell rules.
.table.compactDensityTighter cell padding; font size and icons unchanged.

Basic usage

Team members
Name Email 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.

Revenue by month
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.

Standings by position
Pos Name
1John Nemo
2Jane Doe
3Ada 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; .compact re-declares it tighter.
  • --table-min — minimum table width before .table-wrap scrolls 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).
Actual CSS

Search documentation