J3W1 / DESIGN SYSTEM
Table
A native data table with a header rule, row dividers, hover and selection fills, sortable headers with a glyph, and static empty and loading rows.
stable · LIGHT DOM · 1.1.0
Light DOM retains static content, native semantics, label relationships and host composition without duplicating controls.
Live variants
default
| Atlas library | Avery Kim | Ready | 24 |
| Orion workspace | Jordan Lee | In progress | 12 |
| Field notes | Morgan Noor | Ready | 38 |
| Signal monitor | Riley Stone | Review | 6 |
| No rows match. | |||
| ⋯ Loading rows | |||
<j3w1-table id="example-table-default"><div class="table-scroller" tabindex="0" role="region" aria-label="Project records"><table class="table"><caption class="table-caption">Project records · synthetic fixture</caption><thead class="table-head"><tr><th class="table-header" scope="col"><button class="table-sort" type="button">Project<span class="table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="table-header" scope="col"><button class="table-sort" type="button">Owner<span class="table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="table-header" scope="col"><button class="table-sort" type="button">Status<span class="table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="table-header" scope="col"><button class="table-sort" type="button">Scope<span class="table-sort-glyph" aria-hidden="true">↕</span></button></th></tr></thead><tbody class="table-body"><tr class="table-row" id="example-table-default-record-0" data-status="Ready"><td class="table-cell" id="example-table-default-record-name-0">Atlas library</td><td class="table-cell">Avery Kim</td><td class="table-cell">Ready</td><td class="table-cell">24</td></tr><tr class="table-row" id="example-table-default-record-1" data-status="In progress"><td class="table-cell" id="example-table-default-record-name-1">Orion workspace</td><td class="table-cell">Jordan Lee</td><td class="table-cell">In progress</td><td class="table-cell">12</td></tr><tr class="table-row" id="example-table-default-record-2" data-status="Ready"><td class="table-cell" id="example-table-default-record-name-2">Field notes</td><td class="table-cell">Morgan Noor</td><td class="table-cell">Ready</td><td class="table-cell">38</td></tr><tr class="table-row" id="example-table-default-record-3" data-status="Review"><td class="table-cell" id="example-table-default-record-name-3">Signal monitor</td><td class="table-cell">Riley Stone</td><td class="table-cell">Review</td><td class="table-cell">6</td></tr><tr class="table-empty" hidden=""><td class="table-empty-cell" colspan="4">No rows match.</td></tr><tr class="table-loading" hidden=""><td colspan="4">⋯ Loading rows</td></tr></tbody></table></div></j3w1-table>sticky-header
| Atlas library | Avery Kim | Ready | 24 |
| Orion workspace | Jordan Lee | In progress | 12 |
| Field notes | Morgan Noor | Ready | 38 |
| Signal monitor | Riley Stone | Review | 6 |
| No rows match. | |||
| ⋯ Loading rows | |||
<j3w1-table id="example-table-sticky-header"><div class="table-scroller" tabindex="0" role="region" aria-label="Project records"><table class="table"><caption class="table-caption">Project records · synthetic fixture</caption><thead class="table-head"><tr><th class="table-header" scope="col"><button class="table-sort" type="button">Project<span class="table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="table-header" scope="col"><button class="table-sort" type="button">Owner<span class="table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="table-header" scope="col"><button class="table-sort" type="button">Status<span class="table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="table-header" scope="col"><button class="table-sort" type="button">Scope<span class="table-sort-glyph" aria-hidden="true">↕</span></button></th></tr></thead><tbody class="table-body"><tr class="table-row" id="example-table-sticky-header-record-0" data-status="Ready"><td class="table-cell" id="example-table-sticky-header-record-name-0">Atlas library</td><td class="table-cell">Avery Kim</td><td class="table-cell">Ready</td><td class="table-cell">24</td></tr><tr class="table-row" id="example-table-sticky-header-record-1" data-status="In progress"><td class="table-cell" id="example-table-sticky-header-record-name-1">Orion workspace</td><td class="table-cell">Jordan Lee</td><td class="table-cell">In progress</td><td class="table-cell">12</td></tr><tr class="table-row" id="example-table-sticky-header-record-2" data-status="Ready"><td class="table-cell" id="example-table-sticky-header-record-name-2">Field notes</td><td class="table-cell">Morgan Noor</td><td class="table-cell">Ready</td><td class="table-cell">38</td></tr><tr class="table-row" id="example-table-sticky-header-record-3" data-status="Review"><td class="table-cell" id="example-table-sticky-header-record-name-3">Signal monitor</td><td class="table-cell">Riley Stone</td><td class="table-cell">Review</td><td class="table-cell">6</td></tr><tr class="table-empty" hidden=""><td class="table-empty-cell" colspan="4">No rows match.</td></tr><tr class="table-loading" hidden=""><td colspan="4">⋯ Loading rows</td></tr></tbody></table></div></j3w1-table>numeric
| Atlas library | Avery Kim | Ready | 24 |
| Orion workspace | Jordan Lee | In progress | 12 |
| Field notes | Morgan Noor | Ready | 38 |
| Signal monitor | Riley Stone | Review | 6 |
| No rows match. | |||
| ⋯ Loading rows | |||
<j3w1-table id="example-table-numeric"><div class="table-scroller" tabindex="0" role="region" aria-label="Project records"><table class="table"><caption class="table-caption">Project records · synthetic fixture</caption><thead class="table-head"><tr><th class="table-header" scope="col"><button class="table-sort" type="button">Project<span class="table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="table-header" scope="col"><button class="table-sort" type="button">Owner<span class="table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="table-header" scope="col"><button class="table-sort" type="button">Status<span class="table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="table-header" scope="col"><button class="table-sort" type="button">Scope<span class="table-sort-glyph" aria-hidden="true">↕</span></button></th></tr></thead><tbody class="table-body"><tr class="table-row" id="example-table-numeric-record-0" data-status="Ready"><td class="table-cell" id="example-table-numeric-record-name-0">Atlas library</td><td class="table-cell">Avery Kim</td><td class="table-cell">Ready</td><td class="table-cell">24</td></tr><tr class="table-row" id="example-table-numeric-record-1" data-status="In progress"><td class="table-cell" id="example-table-numeric-record-name-1">Orion workspace</td><td class="table-cell">Jordan Lee</td><td class="table-cell">In progress</td><td class="table-cell">12</td></tr><tr class="table-row" id="example-table-numeric-record-2" data-status="Ready"><td class="table-cell" id="example-table-numeric-record-name-2">Field notes</td><td class="table-cell">Morgan Noor</td><td class="table-cell">Ready</td><td class="table-cell">38</td></tr><tr class="table-row" id="example-table-numeric-record-3" data-status="Review"><td class="table-cell" id="example-table-numeric-record-name-3">Signal monitor</td><td class="table-cell">Riley Stone</td><td class="table-cell">Review</td><td class="table-cell">6</td></tr><tr class="table-empty" hidden=""><td class="table-empty-cell" colspan="4">No rows match.</td></tr><tr class="table-loading" hidden=""><td colspan="4">⋯ Loading rows</td></tr></tbody></table></div></j3w1-table>with-caption
| Atlas library | Avery Kim | Ready | 24 |
| Orion workspace | Jordan Lee | In progress | 12 |
| Field notes | Morgan Noor | Ready | 38 |
| Signal monitor | Riley Stone | Review | 6 |
| No rows match. | |||
| ⋯ Loading rows | |||
<j3w1-table id="example-table-with-caption"><div class="table-scroller" tabindex="0" role="region" aria-label="Project records"><table class="table"><caption class="table-caption">Project records · synthetic fixture</caption><thead class="table-head"><tr><th class="table-header" scope="col"><button class="table-sort" type="button">Project<span class="table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="table-header" scope="col"><button class="table-sort" type="button">Owner<span class="table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="table-header" scope="col"><button class="table-sort" type="button">Status<span class="table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="table-header" scope="col"><button class="table-sort" type="button">Scope<span class="table-sort-glyph" aria-hidden="true">↕</span></button></th></tr></thead><tbody class="table-body"><tr class="table-row" id="example-table-with-caption-record-0" data-status="Ready"><td class="table-cell" id="example-table-with-caption-record-name-0">Atlas library</td><td class="table-cell">Avery Kim</td><td class="table-cell">Ready</td><td class="table-cell">24</td></tr><tr class="table-row" id="example-table-with-caption-record-1" data-status="In progress"><td class="table-cell" id="example-table-with-caption-record-name-1">Orion workspace</td><td class="table-cell">Jordan Lee</td><td class="table-cell">In progress</td><td class="table-cell">12</td></tr><tr class="table-row" id="example-table-with-caption-record-2" data-status="Ready"><td class="table-cell" id="example-table-with-caption-record-name-2">Field notes</td><td class="table-cell">Morgan Noor</td><td class="table-cell">Ready</td><td class="table-cell">38</td></tr><tr class="table-row" id="example-table-with-caption-record-3" data-status="Review"><td class="table-cell" id="example-table-with-caption-record-name-3">Signal monitor</td><td class="table-cell">Riley Stone</td><td class="table-cell">Review</td><td class="table-cell">6</td></tr><tr class="table-empty" hidden=""><td class="table-empty-cell" colspan="4">No rows match.</td></tr><tr class="table-loading" hidden=""><td colspan="4">⋯ Loading rows</td></tr></tbody></table></div></j3w1-table>Install and register
Use the exact version. If npm publication is pending, install the release tarball from the installation guide.
npm install --save-exact @j3w1/ui@1.1.0import '@j3w1/ui/tokens.css';
import '@j3w1/ui/styles/table.css';
import '@j3w1/ui/register/table';Insert the maintained markup above after loading these imports. Its native children provide accessible names and form behavior; the empty custom tag alone does not create a control.
Copy the complete implementation
npx --package=@j3w1/ui@1.1.0 j3w1-ui copy table --out ./vendor/j3w1/tableThe CLI verifies every file before writing and refuses overwrites. Serve the output over HTTP and open index.html. Keep its modules, CSS, tokens and licence notices together.
Public API
attributes
| Name | Type / signature | Meaning |
|---|---|---|
disabled | CustomEvent | Disable the native controls owned by this instance. |
loading | CustomEvent | Mark the primary control busy and prevent duplicate activation. |
properties
| Name | Type / signature | Meaning |
|---|---|---|
disabled | boolean | Delegates disabled state to native children. |
name | string | Native control name; application form keys remain explicit. |
methods
| Name | Type / signature | Meaning |
|---|---|---|
refresh | (): void | Reconnect behavior after replacing native child structure. |
focus | (options?: FocusOptions): void | Focus the first usable native control. |
sort | (column: number, direction?: 'ascending' | 'descending'): void | Sort existing native rows by a column. |
filter | (query: string): void | Filter existing rows by literal text. |
events
| Name | Type / signature | Meaning |
|---|---|---|
j3w1-sort | CustomEvent | The user requested sorting. |
j3w1-selection | CustomEvent | Selected row identities changed. |
Dependencies and verification
Registered components: none. Style dependencies: none.
Package presence establishes an available implementation. Actual passes depend on the tested artifact and environment. Read specification execution evidence; read packed-consumer evidence; inspect the complete state matrix.
Full implementation manifest, API and token dependency closure
Canonical specification
Purpose
Shows records as rows and attributes as columns so they can be compared down a column. The table is for reading; a table with selection checkboxes, resizable columns and a toolbar is the data-table. A single column of rows is a list.
Anatomy
A collapsed native table on color.surface.default with no outer border.
The header row is color.text.bright above a 1px color.border.strong rule
(2px when the header is sticky, on color.surface.raised); sortable headers
wrap their label in a button and show ▲ or ▼ when sorted. Body rows are the
row height, separated by 1px color.border.divider, text in
color.text.default; numeric cells are end-aligned with tabular numbers.
An optional caption in color.text.muted sits above. Two special rows,
hidden unless needed, span every column: the empty message and the loading
glyph.
States
| State | Visual | Non-colour channel |
|---|---|---|
| default | rows on color.surface.default; header rule 1px color.border.strong | — |
| hover | row background → color.interaction.hover.bg | cursor: default; only on hover-capable pointers |
| selected | row fill color.interaction.selection.bg; every cell color.interaction.selection.text | aria-selected="true" on the row |
| selected+focus-visible | the fill and a ring in color.interaction.focus.ring-container around the row | both visible at once |
| selected+container-inactive | row fill color.interaction.selection.inactive-bg with color.interaction.selection.inactive-text; no ring | lightness drop between the two fills |
| sorted | the sorted header shows ▲ (ascending) or ▼ (descending) in color.text.bright | aria-sort; the glyph |
| empty | the body is one full-width cell in color.text.muted: “No rows match.” | the message text |
| loading | the body is one full-width cell with the static ⋯ glyph in color.text.muted; the header stays | aria-busy="true"; the glyph; no animation |
Precedence: loading > empty; selected > hover; focus-visible is always drawn.
Keyboard
A plain table has no row focus: Tab reaches the sort buttons in the header and any control inside a cell. Enter or Space on a sort button cycles ascending, descending. Tables whose rows are targets (row links, row selection) are the data-table and add row focus there.
Accessibility
caption or aria-labelledby names the table; th scope="col" on every
header and scope="row" on a row header column. aria-sort on the sorted
th, none or absent elsewhere; the glyph is aria-hidden. The empty and
loading rows are real rows so the table stays a table; aria-busy="true"
on the table while loading and a live region for the outcome. Contrast:
body text 8.43:1, header 10.10:1, caption and empty message 5.66:1, selected
text 12.47:1 on the fill, inactive 7.02:1, ring 7.48:1 on the fill, sort ring
4.57:1. Rows are at least 24 CSS pixels tall; sort buttons fill the header
cell.
Portability
Row backgrounds, one border under the header, one between rows, an outline on a selected row. Toolkits with a native table view take the rule, the divider and the two fills. Hosts that cannot draw an outline on a row draw the ring as an inset 1px border and record it.
Non-examples
Zebra striping. A rounded or bordered table frame. A selected row marked by
a left bar (that is navigation, not selection). Header text in caption size
or uppercase. Sorting shown by colour alone without aria-sort and a glyph.
An animated spinner while loading. An empty state that removes the header.
Text truncated with no way to read the full value. A hover fill on a
touch device.