Table
Rows of data on hairlines, with sortable headings and aligned figures.
On this page
A table sets rows of data on hairlines. Column headings are quiet captions on a firmer rule, and the rows carry the ink. Put table on the <table>, table-row on each <tr>, table-heading on column headings and table-cell on data cells and row headings. The table has no surface of its own, so put it in a sheet or a card.
Table
<div class="sheet overflow-hidden"> <table class="table"> <thead> <tr class="table-row"> <th class="table-heading" scope="col">Project</th> <th class="table-heading" scope="col">Status</th> <th class="table-heading" scope="col">Region</th> <th class="table-heading table-numeric" scope="col">Requests</th> </tr> </thead> <tbody> <tr class="table-row"> <th class="table-cell table-primary" scope="row">Atlas</th> <td class="table-cell"> <span class="badge badge-success badge-dot">Ready</span> </td> <td class="table-cell">Tokyo</td> <td class="table-cell table-numeric">1,284,019</td> </tr> <tr class="table-row"> <th class="table-cell table-primary" scope="row">Borealis</th> <td class="table-cell"> <span class="badge badge-warning badge-dot">Degraded</span> </td> <td class="table-cell">Frankfurt</td> <td class="table-cell table-numeric">94,201</td> </tr> <tr class="table-row"> <th class="table-cell table-primary" scope="row">Cirrus</th> <td class="table-cell"> <span class="badge badge-dot">Draft</span> </td> <td class="table-cell">Virginia</td> <td class="table-cell table-numeric">0</td> </tr> </tbody> </table></div>#Cells
Use <th class="table-cell" scope="row"> when a cell names its row, or scope="rowgroup" when it names a group. It aligns to the start and uses --font-weight-control, as does a table-cell with role="rowheader" in a grid of roles. Ordinary cells keep the surrounding weight; the browser's centered, bold <th> default does not change the table's rhythm.
The same classes supply table, row and cell display for a table of ARIA roles, including with standalone CSS.
table-primarymarks the cell that identifies the row and gives it strong ink. The other cells stay in body ink, so the eye finds the name first.table-numericon a heading and its cells sets them on the end edge in tabular figures. Digits then stack in columns and amounts can be compared by length.
#Sorting
A heading with aria-sort="ascending" or aria-sort="descending" takes strong ink and a chevron that points the way the values run. Put a button inside the heading to change the order; the stylesheet reads the attribute, and your script or your server sets it.
Sorting
<div class="sheet overflow-hidden"> <table class="table table-compact"> <thead> <tr class="table-row"> <th class="table-heading" scope="col" aria-sort="ascending">Name</th> <th class="table-heading" scope="col">Role</th> <th class="table-heading table-numeric" scope="col">Deployments</th> </tr> </thead> <tbody> <tr class="table-row"> <td class="table-cell table-primary">Ada Lovelace</td> <td class="table-cell">Owner</td> <td class="table-cell table-numeric">412</td> </tr> <tr class="table-row"> <td class="table-cell table-primary">Alan Turing</td> <td class="table-cell">Admin</td> <td class="table-cell table-numeric">96</td> </tr> <tr class="table-row"> <td class="table-cell table-primary">Grace Hopper</td> <td class="table-cell">Member</td> <td class="table-cell table-numeric">1,204</td> </tr> </tbody> </table></div>table-compact tightens the rows, for long lists that are read by scanning.
#Selection and rows that open
A row with aria-selected="true" takes a chip tone. table-interactive on the table makes every body row answer the pointer at once, the way a row in a menu does. The row paints these tones, not its cells, so no seam shows where two cells meet.
Selection
<div class="sheet overflow-hidden"> <table class="table table-interactive"> <thead> <tr class="table-row"> <th class="table-heading" scope="col"> <input type="checkbox" class="checkbox" aria-label="Select all"> </th> <th class="table-heading" scope="col">Domain</th> <th class="table-heading" scope="col">Certificate</th> </tr> </thead> <tbody> <tr class="table-row" aria-selected="true"> <td class="table-cell"> <input type="checkbox" class="checkbox" checked aria-label="Select atlas.dev"> </td> <td class="table-cell table-primary">atlas.dev</td> <td class="table-cell">Renews in 61 days</td> </tr> <tr class="table-row"> <td class="table-cell"> <input type="checkbox" class="checkbox" aria-label="Select docs.atlas.dev"> </td> <td class="table-cell table-primary">docs.atlas.dev</td> <td class="table-cell">Renews in 12 days</td> </tr> </tbody> </table></div>#Wide tables
A table keeps its columns: it does not restack into cards, because a restacked table loses the comparison a table exists for. When it can be wider than its column, wrap it in a scroll area that scrolls along the inline axis; the edge fades where columns continue. table-sticky keeps the headings in view while rows scroll under them.
Wide tables
<div class="sheet overflow-hidden"> <div class="scroll-area scroll-area-inline" tabindex="0" role="region" aria-label="Deployments"> <table class="table"> <thead> <tr class="table-row"> <th class="table-heading" scope="col">Deployment</th> <th class="table-heading" scope="col">Branch</th> <th class="table-heading" scope="col">Commit</th> <th class="table-heading" scope="col">Author</th> <th class="table-heading table-numeric" scope="col">Duration</th> </tr> </thead> <tbody> <tr class="table-row"> <td class="table-cell table-primary whitespace-nowrap">atlas-9f2c1a</td> <td class="table-cell whitespace-nowrap">main</td> <td class="table-cell whitespace-nowrap">Soften every edge</td> <td class="table-cell whitespace-nowrap">Ada Lovelace</td> <td class="table-cell table-numeric">48s</td> </tr> <tr class="table-row"> <td class="table-cell table-primary whitespace-nowrap">atlas-71be0d</td> <td class="table-cell whitespace-nowrap">feature/anchors</td> <td class="table-cell whitespace-nowrap">Glide the segmented thumb</td> <td class="table-cell whitespace-nowrap">Alan Turing</td> <td class="table-cell table-numeric">1m 02s</td> </tr> </tbody> </table> </div></div>#Accessibility
- Use a real
<table>with<thead>,<tbody>andscope="col"on headings, so each cell is announced with its column. - A scrolling wrapper needs
tabindex="0",role="region"and a label, so the keys can scroll it. - Selection is said twice on purpose:
aria-selectedon the row and the state of its checkbox.
#Classes
| Class | Description |
|---|---|
table | The table: a native table or a grid of roles |
table-row | A row |
table-heading | A column heading: a quiet caption on a firmer rule than the rows |
table-cell | A start-aligned data cell or row header. A native scope=row/rowgroup or role=rowheader uses the theme’s control weight instead of browser bold |
table-primary | The identifying cell of a row, in strong ink. With table-cell |
table-numeric | Right-aligned tabular figures, on a heading or a cell. With table-heading or table-cell |
table-compact | Denser rows, for long lists read by scanning. With table |
table-interactive | Rows that hold cells answer the pointer and show a focus ring. With table |
table-sticky | Headings stay in view while the rows scroll under them. With table |
#Custom properties
| Property | Default | Description |
|---|---|---|
--table-padding-x | 1rem | Inline padding of headings and cells; .75rem in a compact table |
--table-padding-y | .75rem | Block padding of cells; .4375rem in a compact table |