Table

Lays out rows of data you read across, with the numbers lined up.

Examples

Recent transactions

Your 3 most recent transactions
MerchantAmount
Corner Coffee−$4.80
Metro Transit−$32.00
Paycheck$1,850.00

A split bill

ItemYour share
Dinner$32.00
Tickets$15.00
Total$47.00

With a state

BillAmountState
Rent$1,200.00Paid
Phone$45.00Due

States

  • MerchantCategoryAmountState
    Corner CoffeeDining−$4.80Paid

    rest

  • MerchantCategoryAmountState
    Corner CoffeeDining−$4.80Paid

    row hover

  • MerchantCategoryAmountState
    Corner CoffeeDining−$4.80Paid

    row selected

  • MerchantCategoryAmountState
    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
Table
table props
TableRow data-state
'selected'
TableHead, TableCell
th, td props
TableCaption
caption 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

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.