Table
Table styles a real <table> you write and makes it scrollable by keyboard when it is too wide.
| Region | Q1 | Q2 |
|---|---|---|
| North | 1,204 | 1,310 |
| South | 987 | 1,045 |
| Total | 2,191 | 2,355 |
<tk-table>
<table>
<caption>Quarterly revenue by region</caption>
<thead><tr><th scope="col">Region</th><th scope="col" data-numeric>Q1</th><th scope="col" data-numeric>Q2</th></tr></thead>
<tbody>
<tr><th scope="row">North</th><td data-numeric>1,204</td><td data-numeric>1,310</td></tr>
<tr><th scope="row">South</th><td data-numeric>987</td><td data-numeric>1,045</td></tr>
</tbody>
<tfoot><tr><th scope="row">Total</th><td data-numeric>2,191</td><td data-numeric>2,355</td></tr></tfoot>
</table>
</tk-table>Anatomy
| Part | What it is |
|---|---|
tk-table |
the bordered, rounded container |
| default slot | your <table>, with <caption>, <thead>, <tbody>, and <tfoot> |
<caption> |
the title band; it names the table |
<thead> |
the header row, in medium secondary text |
<th scope="row"> |
a row header, in medium text |
<tfoot> |
the total row, in semibold text |
scroll part |
the scroll container around the table |
Density
| Service | Status |
|---|---|
| Payments | Investigating |
| Search | Resolved |
<tk-table density="compact">
<table>
<caption>Open incidents</caption>
<thead><tr><th scope="col">Service</th><th scope="col">Status</th></tr></thead>
<tbody><tr><td>Payments</td><td>Investigating</td></tr><tr><td>Search</td><td>Resolved</td></tr></tbody>
</table>
</tk-table>density |
Cell padding |
|---|---|
comfortable (default) |
space-12 × space-16 |
compact |
space-8 × space-12 |
Options
| Name | Role |
|---|---|
| Alice Chen | Design |
| Bo Nguyen | Engineering |
| Carla Diaz | Research |
<tk-table zebra hover header-fill>
<table>
<caption>Team members</caption>
<thead><tr><th scope="col">Name</th><th scope="col">Role</th></tr></thead>
<tbody><tr><td>Alice Chen</td><td>Design</td></tr><tr><td>Bo Nguyen</td><td>Engineering</td></tr><tr><td>Carla Diaz</td><td>Research</td></tr></tbody>
</table>
</tk-table>zebra: shades every other row. Use it on wide, dense tables.hover: highlights the row under the pointer. It can suggest a row is clickable, so use it only when it helps people follow a row.header-fill: fills the header row with a subtle band. By default the header has no fill.
Zebra rows and hover are decoration. Headers and cell text carry the meaning.
Sticky header
sticky keeps the header row in view as the table scrolls. It needs a maximum height on the
scroll part.
<style>
tk-table::part(scroll) { max-height: 320px; }
</style>
<tk-table sticky>
<table>…</table>
</tk-table>The class
The tk-table class styles a table without the element. It takes the same options as
data-* attributes, but has no scroll region: a table wider than its container is cut off.
Use <tk-table> for any table that can overflow.
| Action | Keys |
|---|---|
| Search | Ctrl K |
| Save | Ctrl S |
<div class="tk-table" data-density="compact" data-zebra>
<table>
<caption>Shortcuts</caption>
<thead><tr><th scope="col">Action</th><th scope="col">Keys</th></tr></thead>
<tbody><tr><td>Search</td><td>Ctrl K</td></tr><tr><td>Save</td><td>Ctrl S</td></tr></tbody>
</table>
</div>When to use
- Use a table for data in rows and columns with headers.
- Use a
Data table to sort, select, paginate, or expand rows. Table only styles markup; it renders nothing from data. Both look the same. - A plain markdown table in
Prose needs nothing. Use Table when you need density, zebra rows, a sticky header, or keyboard scrolling. - Don't use a table for layout.
Writing the table
Table styles your markup and never changes its structure.
- Give every table a
<caption>, or alabelif it has none. It names the table and its scroll region. - Mark headers with
scope:<th scope="col">for columns,<th scope="row">for rows. - Add
data-numericto number cells and their header. They align to the end and the digits line up. - Use
data-align="center"ordata-align="end"to align other cells.
Do / Don't
| ✅ Do | ❌ Don't |
|---|---|
Write <table> markup with a <caption> and scope |
Build a table from divs, or drop the caption |
Mark number columns with data-numeric |
Leave numbers aligned to the start |
Use <tk-table> when the table can be wider than its container |
Put a wide table in the bare class, where it gets cut off |
Set a max-height on ::part(scroll) for sticky |
Expect sticky to work without a height |
| Use a |
Add sorting or selection to Table by hand |
Related
Data table : the same look, rendered from data, with sorting, selection, and pagination.Prose : styles plain markdown tables.Scroll area : a scrollable region for other content.
API
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
density |
comfortable | compact |
comfortable |
cell padding |
zebra |
boolean | false |
shades every other row |
hover |
boolean | false |
highlights the row under the pointer |
header-fill |
boolean | false |
fills the header row |
sticky |
boolean | false |
keeps the header row in view; needs a height on ::part(scroll) |
label |
string | names the scroll region; the <caption> is used if unset |
Slots
| Slot | Content |
|---|---|
| default | one <table> |
Cell attributes
| Attribute | On | What it does |
|---|---|---|
data-numeric |
th, td |
aligns to the end, with tabular figures |
data-align |
th, td |
center or end |
Events
None.
CSS
::part(scroll): the scroll container. Setmax-heighton it for a sticky header.
Tokens
| Element | Tokens |
|---|---|
| container | border/subtle, radius-12 |
| caption | text/default, medium |
| header | text/secondary, medium; with header-fill, surface/subtle |
| row header | text/default, medium |
| cells | text/default, Body Small (14/20) |
| row rules, total rule | border/subtle |
| total row | text/default, semibold |
| zebra row | surface/subtle |
| hovered row | state/hover |
| sticky header | surface/default, or surface/subtle with header-fill |
The focus ring is border/accent/focus. Values per theme:
Accessibility
Built in:
- Your table markup stays as written, so
<caption>,<thead>, andscopework for screen readers. - When the table is wider than its container, the scroll container becomes a Tab stop and
a region named by the caption or
label. When the table fits, it is neither. - The focus ring sits inside the container, so a card or dialog around the table can't clip it, and a sticky header can't cover it.
- A sticky header changes nothing for screen readers.
Your part:
- Write
<caption>,<th scope="col">, and<th scope="row">. Table doesn't add them. - Name a table without a caption with
label. - Don't put meaning in zebra rows or color alone.
| Key | Action |
|---|---|
Tab |
moves focus to the scroll container, when the table overflows |
← → ↑ ↓ |
scroll the focused table |