skip to content
menu
components · table cells

table cells

Table cells are three ready-made cells for the columns of a data table, so every table says the same things the same way. The status cell shows a status and its reason, the date cell a date and how long ago it was, and the identifier cell what names the row.

episodeairedstate
S04E07The Long Way Round, 5 days agomissingnothing found · again in 6h
S04E08Low Tide, yesterdayerror: needs attentionno release passed the quality profile: every one was below 720p
S04E09no dateon its waywaiting for the catalog

when to use

  • Status cell. For the state column of a data table: one status per row, from the object's one table of states, with the reason or the next step under it when there is one.
  • Date cell. For a date column where age matters: “aired”, “last searched”, “created”, “expires”.
  • Identifier cell. For the row header column: what people call the row, with a quieter second line that says more.

when to use something else

  • For a status outside a table, such as beside a heading, in a card or in a sentence. Use a status on its own. The status cell only adds the detail line, sized for a row.
  • For a message about the whole list, such as “105 titles can't be checked”. That is a banner above the table, not a status in every row.
  • For tags, kinds and categories. They aren't states. Use a badge.
  • For numbers and sizes. Use a plain column, aligned to the right, with the unit in its header. The content rules of the table show how.

anatomy

status cell

  1. Wrapper. The status, with its detail close under it, at the cell's leading edge. It is never wider than the cell.
  2. Status. A status: a small chip in the state's colour, with the state's glyph and a few words in lowercase monospace, on one line.
  3. Detail (optional). A quieter line under the status, in small monospace, at most 44 characters wide. At most two lines show. It can break anywhere, so a long code can't widen the column.

date cell

  1. Wrapper. The date and how long ago it was, side by side on one line; in a narrow column, the second part wraps under the date. Both are in monospace with tabular figures, so dates line up down a column.
  2. Date. The date itself, in lowercase: “12 oct 2026”, with the day, the short month and the year. Resting the pointer on it shows the full weekday, date and time.
  3. How long ago (on unless you turn it off). In a quieter colour: “5d ago” on screen, “5 days ago” for a screen reader.
  4. Empty. A dash in the quieter colour, read as “no date”.

identifier cell

  1. Wrapper. The title and the text, one above the other, close together.
  2. Title. What people call the row, in monospace and the brightest text colour: “S04E07”, “worker-2”.
  3. Text (optional). A second line in the body face, smaller and quieter, on one line. It ends in an ellipsis when its box is narrower than the text.

variants and states

status cell

The state sets the colour, the glyph and what a screen reader hears first; there is no setting for the colour. The five states, as the status vocabulary uses them:

state means for instance
Neutral A plain fact “missing”
Information Under way, or worth knowing “on its way”
Success Done and good, and not for a problem “held”
Warning Works, but someone should look “can't be checked”
Error Failed or blocked; needs action “needs attention”

The detail is the line under the status: why it is so, or what happens next. Like every status, the one in a cell can also be shown as text: without the chip, for dense rows, keeping the glyph and slightly larger words.

missingnothing found · again in 6hon its waywaiting for the catalogerror: needs attentionno release passed the quality profile: every one was below 720pheld

date cell

How long ago counts whole calendar days, in the viewer's time zone:

how far on screen read as
The same day “today” “today”
One day “yesterday”, “tomorrow” “yesterday”, “tomorrow”
2 to 13 days “5d ago”, “in 3d” “5 days ago”, “in 3 days”
14 to 59 days “7w ago” “7 weeks ago”
60 days to just under two years “20mo ago” “20 months ago”
Two years or more “2y ago” “2 years ago”
, today, yesterday, 5 days ago, 7 weeks ago, 20 months ago, in 4 daysno date

identifier cell

The title is what identifies the row; the text is a second line, when there is one to give. The title can be a link to the row's own page.

S04E07The Long Way RoundS04E08worker-2Rack 4 · zone B

behaviour

  • The cells are text. None of them takes focus or handles a key: the keys belong to the row they sit in, and the data table handles them. tab passes the cells by, unless an identifier's title is a link.
  • The date cell works out “today” when it is drawn. It doesn't update on its own: a page left open past midnight says “today” about yesterday, until the cell is drawn again.
  • The texts that show under the pointer, a status detail in full and a date's full time, are the browser's own tooltips.

The full keyboard map is on the table cells' React page.

accessibility

  • Status cell. Screen readers skip the glyph: the words carry the state, and warnings and errors are read with their state first, as in “error: needs attention”. The detail is there in full for a screen reader, so it reads all of it, including the lines cut off on screen.
  • Date cell. The date is read as it is written, “12 Oct 2026”, because the lowercase is only a style. Then comes how long ago, in words: “5 days ago”. Screen readers skip the short “5d ago”. An empty value is read as “no date”.
  • Identifier cell. Plain text. As the row header, it names the row for every other cell in it.

Today:

  • A status detail longer than two lines, and a date's full time, show only in the browser's own tooltip: under a mouse pointer. People who use a keyboard or a touch screen can't open it. Screen readers read the whole detail, but the time of day is only in that tooltip, which most of them don't read for plain text.
  • A detail that isn't plain text, such as one that holds a link, gets no tooltip at all, so the lines that are cut off can't be seen on screen.
  • The spoken words are fixed, in English: “no date”, and the words for how long ago, such as “5 days ago”, whatever the language of the dates.
  • An identifier's second line that doesn't fit is cut short with an ellipsis, and has no tooltip.

content

  • Status. The state in the object's own words, one to three words: “missing”, “on its way”, “needs attention”. Write the vocabulary once, as the status vocabulary describes, and use it in every row.
  • Detail. Why, or what happens next, on one short line: “nothing found · again in 6h”, “waiting for the catalog”. A reason a person understands, not a raw error or a code.
  • Dates. Give the cell the date itself and let it write the date out; don't turn the date into text beforehand. Keep how long ago where it matters how recent a date is, as for “aired” or “last searched”. Turn it off where it doesn't, as for a fixed release date.
  • Identifier. The title is what people call the row, “S04E07” or “worker-2”; the text is its name, spelled the way the name is spelled: “The Long Way Round”. Don't use an internal id, a long string of letters and digits, as the title.

do and don't

do
error: needs attentionno release passed the quality profile
The state in a few words, and the reason on the line under it.
don't
error: failed: no release passed the quality profile, every one was below 720p
The reason crammed into the status: the chip is cut off, and no two rows read alike.
do
, 5 days ago
The date, and how long ago, at a glance.
don't
A time to the second, and nothing that says how long ago: people do the sums for every row.
do
worker-2Zone B · 4 CPU
The name people use as the title, and what else is known on the quiet line.
don't
3f2a1000-5b7d-4c11-9e02-6a1d0b8f4c21worker-2
An internal id as the title: nobody can say it, and the name people know is on the quiet line.
  • Data table: the table these cells are made for.
  • Status: the status inside a status cell, on its own.
  • Status vocabulary: how one table of states keeps every status cell honest.

specs

cell part spec
Status (StatusCell) Wrapper A flex column with 3px between its lines, its items at the start, max-width: 100%, min-width: 0.
Status Status A status: a 20px chip with 8px side padding, a 1px --state-*-border, a --state-*-bg fill and --state-* text, the state's 12px glyph, JetBrains Mono 11px, lowercase, on one line. As text (appearance="text"): no chip, the glyph and 12px text.
Status Detail detail. JetBrains Mono 11px, line-height 1.45, --fg-dim, max-width: 44ch, clamped to two lines (-webkit-line-clamp: 2), overflow-wrap: anywhere. In the page in full. A string detail is also its title, the native tooltip; a detail that isn't a string gets none.
Date (DateCell) Wrapper inline-flex, wrapping, on one baseline, the parts 8px (--s-2) apart. JetBrains Mono 12px, font-variant-numeric: tabular-nums.
Date Date A <time> with the ISO datetime, in --fg-2, lowercased with text-transform. Its title holds the full weekday, date and time (dateStyle: 'full', timeStyle: 'short').
Date How long ago relative (default true), in --fg-dim. The short form is aria-hidden; the long form is visually hidden.
Date Empty emptyText, by default an em dash (—), aria-hidden, in --fg-dim, and a visually hidden “no date”.
Identifier (IdentifierCell) Wrapper An inline-flex column with 2px between its lines, min-width: 0.
Identifier Title title, any node, a link included. JetBrains Mono 13px, --fg.
Identifier Text text. Inter 12px, --fg-muted, on one line (white-space: nowrap), with text-overflow: ellipsis and no tooltip.
date setting in code default
The date value: a Date, an ISO string or epoch milliseconds An empty or unreadable value shows the empty text
Language of the date locale 'en-GB': “12 oct 2026”
Format format, an Intl.DateTimeFormatOptions Day, short month, year: { day: 'numeric', month: 'short', year: 'numeric' }
How long ago relative true
Today now The current time; pinned in stories and tests
  • How long ago, the rule: whole calendar days in the viewer's time zone. Under 14 days it counts days, under 60 weeks (days ÷ 7), under 730 months (days ÷ 30.44), then years (days ÷ 365.25), each rounded. relativeDay(then, now) is the same wording as a function, for a date outside a cell: it returns { short: '5d ago', long: '5 days ago' }.
  • Fixed words: emptyText changes only the visible dash, and locale only the absolute date; “no date” and the words for how long ago stay English.
  • Status cell props: every status prop (state, text or children, icon, appearance, stateLabel) and detail. Spread a vocabulary entry in: <StatusCell {...BACKLOG_STATES[row.state]} detail={why(row)} />.
  • Identifiers: an internal id, such as a UUID, is never the title.
  • Focus: none in any cell, unless an identifier's title is a link.