Table
Data grid for rows × columns of structured content. Mirrors Figma node 140-5139 (light) + 5892-39544 (dark) 1:1. Header strip is #F6F6F6 (light) / #2D2D2D (dark) with Body 1 Semi Bold text. Body rows are transparent (the page surface shows through) with Body 1 Medium text. Every cell carries a 5%-alpha hairline border that hashes the grid without competing with content. Body cells typically pair their label with a trailing kebab (⋮) action via the .table-cell + .table-cell-action helpers. The outer .table-wrap clips the corners to 8 px — required because border-radius can't apply to <table> while border-collapse: collapse is in use.
When to use
- Use for structured data with multiple attributes per row (lists of users, cases, documents)
- Always wrap in
.table-wrap— required for border-radius and overflow on narrow screens - Use
.td-numfor numeric columns to right-align and apply monospace font - Use
.table-cell-actionfor the trailing kebab (⋮) button on each row - Use
<thead>with<th scope="col">so screen readers can associate cells with headers
- Use for a list of items with only one attribute — use a simple list instead
- Use bare
.tablewithout.table-wrap— border-radius won't apply - Simulate a header row with bold
<td>elements — always use<th> - Show more than 8 columns on a single page without a horizontal scroll wrapper
DigiLawyer Patterns
No specific DigiLawyer patterns documented yet — to be added.
Anatomy
A Table is always wrapped in .table-wrap which clips the corners. The table itself uses standard HTML semantics.
| Name | Status |
|---|---|
Item A | Active |
Item B | Pending |
- 1Scroll wrapper —
.table-wrap. Required. Clips to 8px radius + handles horizontal overflow. - 2Table —
.table. Sets collapse, borders, and sizing. - 3Header row —
<thead><tr><th scope="col">. Coloured bg, Semi Bold text. - 4Body cell —
.table-cell. Flex row with label + optional action button. - 5Action slot —
.table-cell-action. Pushes the kebab button to the trailing edge.
Default
A 6-column table with a Header row + 4 Body rows. Each body cell pairs the label with a trailing kebab (⋮) button — the "more actions" affordance Figma calls out on every body cell.
| Label | Label | Label | Label | Label | Label |
|---|---|---|---|---|---|
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Label |
Structure
The component is a real HTML <table>. Wrap it in a .table-wrap div to round the outer corners. Header cells live in <thead><tr><th>; body cells in <tbody><tr><td>. Use the .table-cell flex wrapper inside a <td> when you want a label + trailing action — display: flex can't sit on the td itself without breaking table layout. Drop the .table-cell-action button when a column doesn't need an action.
<div class="table-wrap">
<table class="table">
<thead>
<tr><th>Name</th><th>Email</th><th>Role</th></tr>
</thead>
<tbody>
<tr>
<td>
<div class="table-cell">
Adesh Singh
<button type="button" class="table-cell-action" aria-label="More">
<svg><use href="#ico-kebab"/></svg>
</button>
</div>
</td>
<td>adesh@example.com</td>
<td>Owner</td>
</tr>
</tbody>
</table>
</div>
Composition
Body cells hold any inline content — text, links, badges, tags, status dots. The trailing kebab action is optional; drop the button when a column doesn't need it. Cells without the .table-cell flex wrapper render as plain text + the parent td's padding (use for simple text columns).
| Name | Status | Plan | ||
|---|---|---|---|---|
| Adesh Singh | Online | Pro | adesh@example.com | |
| Jane Doe | Idle | Free | jane@example.com | |
| Mark Patel | Offline | Pro | mark@example.com |
States
The base table has no row hover / selected states in the Figma spec — Figma keeps the surface quiet so content stays primary. The one interactive element is the per-cell kebab action, which fades a soft hover background. .is-hover forces the hover visual at rest for documentation; the live :hover pseudo-class handles it in production.
| State | Kebab |
|---|---|
| Default | |
| Hover (forced) |
Full matrix
Cell content type × row position. Toggle the theme switch in the global header to see every cell under dark tokens — header bg flips #F6F6F6 → #2D2D2D, body bg stays transparent, all borders flip from 5%-black to 5%-white.
| Header text | With kebab | Numeric | Tag | Status dot | Action |
|---|---|---|---|---|---|
| Default row text | With kebab |
1,248 | Active | Online | |
| Long-content row | Long label here |
42 | Pending | Idle | |
| Compact | Item |
7 | Archived | Offline |
Responsive
Wrap any .table in .table-wrap to enable horizontal scroll on narrow screens. The wrapper adds overflow-x: auto and smooth momentum scrolling on touch devices. No changes are needed to the table itself.
| Name | Status | Role | Plan | Joined | |
|---|---|---|---|---|---|
| Adesh Singh | Active | Owner | adesh@example.com | Pro | 1 Jan 2025 |
| Jane Doe | Idle | Admin | jane@example.com | Free | 15 Mar 2025 |
Frame above simulates a 390 px mobile viewport — scroll the table horizontally.
<div class="table-wrap"> <table class="table">…</table> </div>
Accessibility
| Attribute / Behaviour | Value | Notes |
|---|---|---|
| Column headers | <th scope="col"> | Always use <th> with scope="col" so screen readers associate cells with headers |
| Row headers | <th scope="row"> | Use for the first cell of each row when it identifies the row |
| Caption | <caption> | Provide a caption that describes the table's content for screen readers |
| Sortable columns | aria-sort="ascending" | Set on <th> to communicate current sort direction |
| Action buttons | aria-label="More actions for [row name]" | Each kebab (⋮) button must include context about which row it acts on |
| Keyboard — navigate | Tab | Focusable elements inside cells (buttons, links) are in the natural tab order |
API
| Class / attr | On | Description |
|---|---|---|
| .table-wrap | <div> | Clipping wrapper. Carries the outer 1 px border + 8 px radius (--radius-xs) + overflow: hidden. Required when you want rounded corners — <table> can't have border-radius directly under border-collapse: collapse. |
| .table | <table> | The grid. width: 100%, border-collapse: collapse, Body 1 (16/24) typography. Cells get a 5%-alpha hairline border from --table-body-border. |
| .table thead th | <th> | Header cell. Background --table-header-bg, text --table-header-text, font-weight Semi Bold (600). Padding 8 / 12 px → row height ~40 px (24 line-height + 16 padding). |
| .table tbody td | <td> | Body cell. Background --table-body-bg (transparent), text --table-body-text, font-weight Medium (500). Same padding as header → uniform row height. |
| .table-cell | <div> inside <td> | Flex row that aligns a label + trailing action inside a cell. Justify-content defaults to space-between; add .table-cell-end to right-align for action-only columns. |
| .table-cell-end | .table-cell | Utility that flips justify-content to flex-end. Use on action-only columns where there's no leading label. |
| .td-num | <td> | Numeric-column helper. Applies font-variant-numeric: tabular-nums (so digits align column-by-column) and text-align: right in one shot. |
| .table-cell-action | <button> | Per-cell action button — typically the kebab (More) glyph. 24×24 transparent, fades to the header bg on hover. Pair with aria-label describing the action. |
| .is-hover | .table-cell-action | Documentation helper — forces the hover visual at rest. Not needed in production. |
| --table-header-bg / -text / -border | Token | Header strip bg, label text, and border. Border is a 5% alpha hairline in both modes. |
| --table-body-bg / -text / -border | Token | Body row tokens. Body bg is transparent in both modes so the table reads on top of the page surface. |