Table
Lays out rows of data you read across, with the numbers lined up.
Examples
| Merchant | Amount |
|---|---|
| Corner Coffee | −$4.80 |
| Metro Transit | −$32.00 |
| Paycheck | $1,850.00 |
| Item | Your share |
|---|---|
| Dinner | $32.00 |
| Tickets | $15.00 |
| Total | $47.00 |
| Bill | Amount | State |
|---|---|---|
| Rent | $1,200.00 | Paid |
| Phone | $45.00 | Due |
States
Merchant Category Amount State Corner Coffee Dining −$4.80 Paid rest
Merchant Category Amount State Corner Coffee Dining −$4.80 Paid row hover
Merchant Category Amount State Corner Coffee Dining −$4.80 Paid row selected
Merchant Category Amount State No transactions yet. Your payments show here. empty
Usage
When to use it
- Use
- The same fields for many items, compared across rows: transactions, payments, statements.
- Use
- Data with numbers that need to line up.
- Not for
- A phone list of transactions or settings. Use List.
- Not for
- Mixed content per item, with images and long text. Use Card.
- Not for
- Page layout. Use CSS grid; a table is for data.
Rules
8 rules
- Must
- Set text-end on the head and cells of a numeric column. Because start-aligned amounts put the dollars of $8.50 and $1,240.00 in different columns.
- Must
- Add a TableCaption that says what a standalone table lists. Because a screen reader enters the table with no idea which transactions it holds.
- Must
- Add scope="col" to a column TableHead and scope="row" to a row heading. Because TableHead sets no scope, and scope is what makes a screen reader read Amount with each cell.
- Must
- Show one full-width cell that says what to do when there are no rows. Because headings over nothing look like a table that failed to load.
- Should
- Name a column with one noun in sentence case, as in Amount.
- Never
- Add striped rows or a boxed grid; rows are hairlines and fills are for hover and selected. Because bg-surface is the hover fill, so every other row looks pointed at.
- Must
- Pair data-state="selected" with a checked Checkbox; the wash is not the only cue. Because the lime wash is color alone, and a screen reader hears nothing about the row.
- Should
- Let a table past four columns scroll in its wrapper, or use Cards on a phone.
Accessibility
4 checks
- Rows and cells are not focusable; controls inside a row carry their own states.
- TableCaption names the table; TableHead renders a bare th, so add scope.
- Heading text is 5.4:1 on the page; cell text is 17.6:1.
- The selected row is a fill; styles.css gives it a Highlight outline in forced colors.
Edge cases
6 edge cases
- Long cell text
- Cells wrap and the row grows; headings never wrap.
- Narrow container
- The wrapper scrolls sideways; the page never widens.
- Right to left
- The table mirrors; figures stay left to right with the minus in front.
- No rows
- A header over nothing; render the empty row.
- Loading or error
- No state of its own; keep the header and show a sentence.
- Control in a cell
- It keeps its own size; a 48px row fits a 44px control.
API
4 props
Tabletable propsTableRow data-state'selected'TableHead, TableCellth, td propsTableCaptioncaption props
Tokens
9 tokens
--text-sm- 14px
--fg- #0f0f0d
--fg-3- #6b6a63
--text-xs- 12px
--weight-medium- 500
--line- #e6e4df
--surface- #f6f5f2
--accent-wash- #f5f9c4
--dur-instant- 80ms