Design HubStudio

8. Tables

For data that benefits from comparing across rows. The primitive most easily abused — often reached for when a list of cards would scan better.

Gallery

Canonical table at standard density. Hover any row to see the tint; the third row is selected via aria-selected="true".

NameStatusOwnerUpdated
Plate-A03Readyimad2 min ago
Plate-A04Runningdf4 min ago
Plate-A05Stalledimadjust now
  • Header · uppercase, tracking-wider, semibold, muted color (set on .table th)
  • Row hover · subtle tint via .table-interactive
  • Selected row · aria-selected="true", primary-subtle background
  • Row action · .btn-subtle.btn-icon-only at row end — doesn't compete
  • Status column · composes with badge primitive (Section 4)
When to use each

Three decisions that compound: should this even be a table, how dense, and how do rows interact?

Table vs. card list
NameStatusUpdated
Plate-A03Ready2m
Plate-A04Running4m
Plate-A05Stallednow
Table — uniform structure
Use when every row has the same attributes and the user will scan across rows to compare. Status of N plates, runs of M methods, log of events. Table reads top-to-bottom and left-to-right.
PCR amplification

Two-step protocol for amplifying target DNA across 96-well plates.

Beta · updated 2 days ago
Lid transfer

De-lid source plates and seal destination plates in a single workflow.

Verified · updated 1 hour ago
Card list — rich, heterogeneous items
Use when each item has rich content (descriptions, mixed media, varying metadata) and the user reads each item top-to-bottom. Cards trade scan-ability for depth. A method catalog: cards. A method run log: table.
Density — compact vs. comfortable
RunPlatesStatus
PCR-02496Ready
LID-00924Running
STG-20148Stalled
PCR-02396Ready
Compact (.table-compact)
For numeric or high-cardinality data the user scans quickly — log views, runtime metrics, audit trails. Trades whitespace for information density. Use when the user knows what they're looking for.
RunPlatesStatus
PCR-02496Ready
LID-00924Running
Comfortable (.table-comfortable)
For tables a user reads, not scans — admin views, infrequent configuration, summary data. Trades information density for breathing room and a less-frantic feel. Use when each row deserves attention.
Row interaction — clickable row vs. action menu
NameStatus
Plate-A03Ready
Plate-A04Running
Clickable row
Whole row is the affordance — clicks anywhere navigate to detail or select. Use when there's ONE obvious action per row (open / view / select). Adds hover state via .table-interactive.
NameStatus
Plate-A03Ready
Plate-A04Running
Action menu per row
Explicit overflow icon at row end. Use when there are multiple actions per row (Edit / Duplicate / Delete) or when row click would be ambiguous. Drop .table-interactive — the row isn't clickable.
Drift to avoid

Seven named anti-patterns. Tables are where most products lose their visual discipline — every one of these is common.

  • Don't put Title Case in column headers
    Principle 2
    Convention is uppercase, semibold, --tracking-wider, muted color — baked into .table th. Use Title Case only in card headers or section titles, where the heading is the focus. In tables, the header is scaffolding, not content.
  • Don't zebra-stripe rows
    Principle 4
    Striped backgrounds feel old, fight hover/selected state, and add visual noise without adding meaning. Horizontal dividers between rows do the same scanning job without the cost.
  • Don't draw vertical borders between cells
    Principle 1
    Vertical lines turn a table into a spreadsheet. Reserve them for spreadsheets. UI tables use horizontal dividers only — the column alignment plus typography does the work.
  • Don't right-align text columns
    UX
    Text reads left-to-right; ragged left edges are hard to scan. Right-align only numeric columns and short tokens (durations, counts, percentages) with .table-num, which also applies font-variant-numeric: tabular-nums so digits align cleanly.
  • Don't make hover state saturated
    Principle 4
    A strongly-colored hover competes with the selected-row treatment — both look like "the chosen one." Hover is a subtle tint (--color-bg-hover); selection is the saturated state (--color-primary-subtle). Keep the contrast.
  • Don't ship a table without an empty state
    UX
    An empty table is a missed opportunity. Replace the rows with a deliberate empty state: icon, headline, one-sentence explanation, primary action ("Add first…"). See Section 12.
  • Don't cram every action into its own column
    Principle 1
    "Edit" / "Duplicate" / "Delete" columns triple the width and bury the data. Collapse all per-row actions behind a single .btn-subtle.btn-icon-only overflow menu at the row end. The data is what the user came for.
Composition — method run history

A richer table using everything above: sortable header, mixed text and numeric columns, status badges from Section 4, one selected row, overflow action menus, pagination. Each column alignment is intentional.

MethodRunStartedDurationStatusPlates
PCR amplificationPCR-0242026-05-12 14:324h 12mReady96
Lid transferLID-0092026-05-12 13:150h 47mRunning24
Storage retrievalSTG-2012026-05-12 09:041h 23mWarning48
PCR amplificationPCR-0232026-05-11 18:223h 58mReady96
DecappingDCP-1172026-05-11 11:000h 12mError12
  • text-left, numbers-right Method / Run / Started left-aligned; Duration / Plates right-aligned with tabular-nums
  • sort indicator Chevron next to the sortable column header — visible affordance without being noisy
  • code formatting Run IDs (PCR-024) in mono — signals "identifier, not prose"
  • one overflow per row Subtle icon-only button replaces a column of action labels
  • selection ≠ hover Hover is muted tint; selected row uses primary-subtle — distinguishable at a glance