j3w1 / Data tableVue · HTML · React · Astro

J3W1 / DESIGN SYSTEM

Data table

A native table with controls: a selection column with a mixed select-all, sortable headers, row focus with the ring on the fill, and static resize handles.

stable · LIGHT DOM · 1.1.0

Light DOM retains static content, native semantics, label relationships and host composition without duplicating controls.

Live variants

default

Project records · synthetic fixture
Atlas libraryAvery KimReady24
Orion workspaceJordan LeeIn progress12
Field notesMorgan NoorReady38
Signal monitorRiley StoneReview6

<j3w1-data-table id="example-data-table-default"><div class="table-scroller" tabindex="0" role="region" aria-label="Project records"><table class="data-table"><caption class="table-caption">Project records · synthetic fixture</caption><thead class="data-table-head"><tr><th class="data-table-header" scope="col"><button class="data-table-sort" type="button">Project<span class="data-table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="data-table-header" scope="col"><button class="data-table-sort" type="button">Owner<span class="data-table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="data-table-header" scope="col"><button class="data-table-sort" type="button">Status<span class="data-table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="data-table-header" scope="col"><button class="data-table-sort" type="button">Scope<span class="data-table-sort-glyph" aria-hidden="true">↕</span></button></th></tr></thead><tbody class="data-table-body"><tr class="data-table-row" id="example-data-table-default-record-0" data-status="Ready" aria-selected="false" tabindex="0"><td class="data-table-cell" id="example-data-table-default-record-name-0">Atlas library</td><td class="data-table-cell">Avery Kim</td><td class="data-table-cell">Ready</td><td class="data-table-cell">24</td></tr><tr class="data-table-row" id="example-data-table-default-record-1" data-status="In progress" aria-selected="false" tabindex="-1"><td class="data-table-cell" id="example-data-table-default-record-name-1">Orion workspace</td><td class="data-table-cell">Jordan Lee</td><td class="data-table-cell">In progress</td><td class="data-table-cell">12</td></tr><tr class="data-table-row" id="example-data-table-default-record-2" data-status="Ready" aria-selected="false" tabindex="-1"><td class="data-table-cell" id="example-data-table-default-record-name-2">Field notes</td><td class="data-table-cell">Morgan Noor</td><td class="data-table-cell">Ready</td><td class="data-table-cell">38</td></tr><tr class="data-table-row" id="example-data-table-default-record-3" data-status="Review" aria-selected="false" tabindex="-1"><td class="data-table-cell" id="example-data-table-default-record-name-3">Signal monitor</td><td class="data-table-cell">Riley Stone</td><td class="data-table-cell">Review</td><td class="data-table-cell">6</td></tr><tr class="data-table-empty" hidden=""><td class="data-table-empty-cell" colspan="4">No rows match.</td></tr><tr class="data-table-loading" hidden=""><td colspan="4">⋯ Loading rows</td></tr></tbody></table></div></j3w1-data-table>

selectable

Project records · synthetic fixture
Atlas libraryAvery KimReady24
Orion workspaceJordan LeeIn progress12
Field notesMorgan NoorReady38
Signal monitorRiley StoneReview6

<j3w1-data-table id="example-data-table-selectable"><div class="table-scroller" tabindex="0" role="region" aria-label="Project records"><table class="data-table"><caption class="table-caption">Project records · synthetic fixture</caption><thead class="data-table-head"><tr><th class="data-table-header data-table-header-select" scope="col"><button class="data-table-select-all" type="button" role="checkbox" aria-checked="false" aria-label="Select all rows"><span class="data-table-check-glyph" aria-hidden="true">✓</span></button></th><th class="data-table-header" scope="col"><button class="data-table-sort" type="button">Project<span class="data-table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="data-table-header" scope="col"><button class="data-table-sort" type="button">Owner<span class="data-table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="data-table-header" scope="col"><button class="data-table-sort" type="button">Status<span class="data-table-sort-glyph" aria-hidden="true">↕</span></button></th><th class="data-table-header" scope="col"><button class="data-table-sort" type="button">Scope<span class="data-table-sort-glyph" aria-hidden="true">↕</span></button></th></tr></thead><tbody class="data-table-body"><tr class="data-table-row" id="example-data-table-selectable-record-0" data-status="Ready" aria-selected="false" tabindex="0"><td class="data-table-cell data-table-cell-select"><input class="data-table-checkbox" type="checkbox" aria-labelledby="example-data-table-selectable-record-name-0" name="example-data-table-selectable-value"></td><td class="data-table-cell" id="example-data-table-selectable-record-name-0">Atlas library</td><td class="data-table-cell">Avery Kim</td><td class="data-table-cell">Ready</td><td class="data-table-cell">24</td></tr><tr class="data-table-row" id="example-data-table-selectable-record-1" data-status="In progress" aria-selected="false" tabindex="-1"><td class="data-table-cell data-table-cell-select"><input class="data-table-checkbox" type="checkbox" aria-labelledby="example-data-table-selectable-record-name-1" name="example-data-table-selectable-value"></td><td class="data-table-cell" id="example-data-table-selectable-record-name-1">Orion workspace</td><td class="data-table-cell">Jordan Lee</td><td class="data-table-cell">In progress</td><td class="data-table-cell">12</td></tr><tr class="data-table-row" id="example-data-table-selectable-record-2" data-status="Ready" aria-selected="false" tabindex="-1"><td class="data-table-cell data-table-cell-select"><input class="data-table-checkbox" type="checkbox" aria-labelledby="example-data-table-selectable-record-name-2" name="example-data-table-selectable-value"></td><td class="data-table-cell" id="example-data-table-selectable-record-name-2">Field notes</td><td class="data-table-cell">Morgan Noor</td><td class="data-table-cell">Ready</td><td class="data-table-cell">38</td></tr><tr class="data-table-row" id="example-data-table-selectable-record-3" data-status="Review" aria-selected="false" tabindex="-1"><td class="data-table-cell data-table-cell-select"><input class="data-table-checkbox" type="checkbox" aria-labelledby="example-data-table-selectable-record-name-3" name="example-data-table-selectable-value"></td><td class="data-table-cell" id="example-data-table-selectable-record-name-3">Signal monitor</td><td class="data-table-cell">Riley Stone</td><td class="data-table-cell">Review</td><td class="data-table-cell">6</td></tr><tr class="data-table-empty" hidden=""><td class="data-table-empty-cell" colspan="5">No rows match.</td></tr><tr class="data-table-loading" hidden=""><td colspan="5">⋯ Loading rows</td></tr></tbody></table></div></j3w1-data-table>

resizable

Project records · synthetic fixture
Atlas libraryAvery KimReady24
Orion workspaceJordan LeeIn progress12
Field notesMorgan NoorReady38
Signal monitorRiley StoneReview6

<j3w1-data-table id="example-data-table-resizable"><div class="table-scroller" tabindex="0" role="region" aria-label="Project records"><table class="data-table"><caption class="table-caption">Project records · synthetic fixture</caption><thead class="data-table-head"><tr><th class="data-table-header data-table-header-select" scope="col"><button class="data-table-select-all" type="button" role="checkbox" aria-checked="false" aria-label="Select all rows"><span class="data-table-check-glyph" aria-hidden="true">✓</span></button></th><th class="data-table-header" scope="col"><button class="data-table-sort" type="button">Project<span class="data-table-sort-glyph" aria-hidden="true">↕</span></button><span class="data-table-resize" aria-hidden="true"></span><j3w1-menu><div class="menu"><button class="menu-button" type="button" id="example-data-table-resizable-width-trigger-1" aria-haspopup="menu" aria-expanded="false" aria-controls="example-data-table-resizable-width-menu-1">Width: Project</button><ul class="menu-list" role="menu" id="example-data-table-resizable-width-menu-1" aria-labelledby="example-data-table-resizable-width-trigger-1" hidden=""><li role="none"><button class="menu-item" type="button" role="menuitem" tabindex="-1" data-action="resize:1:narrow">Narrower</button></li><li role="none"><button class="menu-item" type="button" role="menuitem" tabindex="-1" data-action="resize:1:widen">Wider</button></li><li role="none"><button class="menu-item" type="button" role="menuitem" tabindex="-1" data-action="resize:1:reset">Reset width</button></li></ul></div></j3w1-menu></th><th class="data-table-header" scope="col"><button class="data-table-sort" type="button">Owner<span class="data-table-sort-glyph" aria-hidden="true">↕</span></button><span class="data-table-resize" aria-hidden="true"></span><j3w1-menu><div class="menu"><button class="menu-button" type="button" id="example-data-table-resizable-width-trigger-2" aria-haspopup="menu" aria-expanded="false" aria-controls="example-data-table-resizable-width-menu-2">Width: Owner</button><ul class="menu-list" role="menu" id="example-data-table-resizable-width-menu-2" aria-labelledby="example-data-table-resizable-width-trigger-2" hidden=""><li role="none"><button class="menu-item" type="button" role="menuitem" tabindex="-1" data-action="resize:2:narrow">Narrower</button></li><li role="none"><button class="menu-item" type="button" role="menuitem" tabindex="-1" data-action="resize:2:widen">Wider</button></li><li role="none"><button class="menu-item" type="button" role="menuitem" tabindex="-1" data-action="resize:2:reset">Reset width</button></li></ul></div></j3w1-menu></th><th class="data-table-header" scope="col"><button class="data-table-sort" type="button">Status<span class="data-table-sort-glyph" aria-hidden="true">↕</span></button><span class="data-table-resize" aria-hidden="true"></span><j3w1-menu><div class="menu"><button class="menu-button" type="button" id="example-data-table-resizable-width-trigger-3" aria-haspopup="menu" aria-expanded="false" aria-controls="example-data-table-resizable-width-menu-3">Width: Status</button><ul class="menu-list" role="menu" id="example-data-table-resizable-width-menu-3" aria-labelledby="example-data-table-resizable-width-trigger-3" hidden=""><li role="none"><button class="menu-item" type="button" role="menuitem" tabindex="-1" data-action="resize:3:narrow">Narrower</button></li><li role="none"><button class="menu-item" type="button" role="menuitem" tabindex="-1" data-action="resize:3:widen">Wider</button></li><li role="none"><button class="menu-item" type="button" role="menuitem" tabindex="-1" data-action="resize:3:reset">Reset width</button></li></ul></div></j3w1-menu></th><th class="data-table-header" scope="col"><button class="data-table-sort" type="button">Scope<span class="data-table-sort-glyph" aria-hidden="true">↕</span></button><span class="data-table-resize" aria-hidden="true"></span><j3w1-menu><div class="menu"><button class="menu-button" type="button" id="example-data-table-resizable-width-trigger-4" aria-haspopup="menu" aria-expanded="false" aria-controls="example-data-table-resizable-width-menu-4">Width: Scope</button><ul class="menu-list" role="menu" id="example-data-table-resizable-width-menu-4" aria-labelledby="example-data-table-resizable-width-trigger-4" hidden=""><li role="none"><button class="menu-item" type="button" role="menuitem" tabindex="-1" data-action="resize:4:narrow">Narrower</button></li><li role="none"><button class="menu-item" type="button" role="menuitem" tabindex="-1" data-action="resize:4:widen">Wider</button></li><li role="none"><button class="menu-item" type="button" role="menuitem" tabindex="-1" data-action="resize:4:reset">Reset width</button></li></ul></div></j3w1-menu></th></tr></thead><tbody class="data-table-body"><tr class="data-table-row" id="example-data-table-resizable-record-0" data-status="Ready" aria-selected="false" tabindex="0"><td class="data-table-cell data-table-cell-select"><input class="data-table-checkbox" type="checkbox" aria-labelledby="example-data-table-resizable-record-name-0" name="example-data-table-resizable-value"></td><td class="data-table-cell" id="example-data-table-resizable-record-name-0">Atlas library</td><td class="data-table-cell">Avery Kim</td><td class="data-table-cell">Ready</td><td class="data-table-cell">24</td></tr><tr class="data-table-row" id="example-data-table-resizable-record-1" data-status="In progress" aria-selected="false" tabindex="-1"><td class="data-table-cell data-table-cell-select"><input class="data-table-checkbox" type="checkbox" aria-labelledby="example-data-table-resizable-record-name-1" name="example-data-table-resizable-value"></td><td class="data-table-cell" id="example-data-table-resizable-record-name-1">Orion workspace</td><td class="data-table-cell">Jordan Lee</td><td class="data-table-cell">In progress</td><td class="data-table-cell">12</td></tr><tr class="data-table-row" id="example-data-table-resizable-record-2" data-status="Ready" aria-selected="false" tabindex="-1"><td class="data-table-cell data-table-cell-select"><input class="data-table-checkbox" type="checkbox" aria-labelledby="example-data-table-resizable-record-name-2" name="example-data-table-resizable-value"></td><td class="data-table-cell" id="example-data-table-resizable-record-name-2">Field notes</td><td class="data-table-cell">Morgan Noor</td><td class="data-table-cell">Ready</td><td class="data-table-cell">38</td></tr><tr class="data-table-row" id="example-data-table-resizable-record-3" data-status="Review" aria-selected="false" tabindex="-1"><td class="data-table-cell data-table-cell-select"><input class="data-table-checkbox" type="checkbox" aria-labelledby="example-data-table-resizable-record-name-3" name="example-data-table-resizable-value"></td><td class="data-table-cell" id="example-data-table-resizable-record-name-3">Signal monitor</td><td class="data-table-cell">Riley Stone</td><td class="data-table-cell">Review</td><td class="data-table-cell">6</td></tr><tr class="data-table-empty" hidden=""><td class="data-table-empty-cell" colspan="5">No rows match.</td></tr><tr class="data-table-loading" hidden=""><td colspan="5">⋯ Loading rows</td></tr></tbody></table></div></j3w1-data-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.0
import '@j3w1/ui/tokens.css';
import '@j3w1/ui/styles/data-table.css';
import '@j3w1/ui/register/data-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.

Vue SFC, React, Astro and plain HTML integration →

Copy the complete implementation

npx --package=@j3w1/ui@1.1.0 j3w1-ui copy data-table --out ./vendor/j3w1/data-table

The 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

NameType / signatureMeaning
disabledCustomEventDisable the native controls owned by this instance.
loadingCustomEventMark the primary control busy and prevent duplicate activation.

properties

NameType / signatureMeaning
disabledbooleanDelegates disabled state to native children.
namestringNative control name; application form keys remain explicit.
selectedIdsstring[]Stable selected row IDs.

methods

NameType / signatureMeaning
refresh(): voidReconnect behavior after replacing native child structure.
focus(options?: FocusOptions): voidFocus the first usable native control.
sort(column: number, direction?: 'ascending' | 'descending'): voidSort existing native rows by a column.
filter(query: string): voidFilter existing rows by literal text.

events

NameType / signatureMeaning
j3w1-sortCustomEventThe user requested sorting.
j3w1-selectionCustomEventSelected row identities changed.

Dependencies and verification

Registered components: menu. Style dependencies: menu.

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

A table the user works in: selects rows, sorts, resizes columns. The plain table is for reading; this component adds the controls and the keyboard model. It stays a native table so its cells keep their header associations; hosts that need cell-level navigation promote it to a grid and record the change.

Anatomy

The table component’s structure with the same rule, dividers and fills, plus: a selection column whose header holds a select-all with aria-checked true, false or mixed, drawn on color.action.primary.bg with the ✓ or – glyph in color.action.primary.text; native checkboxes in each row on color.surface.input with a 1px color.border.control boundary; rows that can take focus; and in the resizable variant a 1px handle in color.border.default at the inline end of each header that turns color.border.active under the pointer.

States

StateVisualNon-colour channel
defaultrows on color.surface.default; header rule 1px color.border.strong
hoverrow background → color.interaction.hover.bg; a resize handle → color.border.activecursor: default on rows, col-resize on handles
focus-visiblering 1px dashed color.interaction.focus.ring at −2px around the focused row or controlthe ring
selectedrow fill color.interaction.selection.bg; every cell color.interaction.selection.text; the row checkbox checkedaria-selected="true"; the checked box
selected+focus-visiblethe fill and the ring in color.interaction.focus.ring-containerboth visible at once
sorted▲ or ▼ in color.text.bright on the sorted headeraria-sort; the glyph
mixedthe select-all on color.action.primary.bg with the – glypharia-checked="mixed"; the glyph
emptyone full-width cell in color.text.muted: “No rows match.”; the header and controls staythe message text
loadingone full-width cell with the static glyph; the header staysaria-busy="true"; the glyph; no animation

Precedence: loading > empty; selected > hover; focus-visible is always drawn. Selection is a fill; focus is a ring; they never borrow each other’s form.

Keyboard

Header controls first: the select-all, then each sort button. Rows use a roving tabindex so the body costs one tab stop; Up and Down move, Space toggles, Shift extends, Ctrl+A selects all. Controls inside the focused row are reached with Tab and left with Shift+Tab. Column widths are set from a header menu or by dragging the handle; the handle itself is never a tab stop.

Accessibility

Every row checkbox is labelled by the row’s first data cell (aria-labelledby); the select-all is named “Select all rows” and exposes aria-checked="mixed" when only some are selected. aria-selected on the row mirrors its checkbox. Sorted headers carry aria-sort; the table carries aria-busy while loading; the count of selected rows is announced through a live region owned by the surrounding toolbar. Contrast: text 8.43:1, header 10.10:1, selected text 12.47:1 on the fill, ring 4.57:1 on the surface and 7.48:1 on the fill, checkbox boundary 4.33:1, check glyph 9.28:1 on the primary fill. Checkboxes and handles are at least 24 CSS pixels tall in compact density.

Portability

The table component’s drawing plus a checkbox column and a header checkbox; the handle is a 1px rule. Toolkits with native table views map row selection to their selection model and the mixed state to their tristate checkbox. Hosts without row focus fall back to the checkbox as the only row control and record it.

Non-examples

A grid role on a table that is not navigated cell by cell. Zebra stripes. A select-all that is a native checkbox with a script-only indeterminate state and no aria-checked. Rows selected by clicking anywhere with no checkbox alternative. A selected row shown by a border alone. A resize handle that is a tab stop. An animated loading skeleton. Rounded checkboxes. Text truncated with no full-value affordance.

j3w1 / command

  • SPEC Overview
  • SPEC Foundations
  • SPEC Components
  • SPEC Patterns
  • SPEC Tokens
  • SPEC Tools
  • SPEC Implement
  • SPEC For agents
  • SPEC Ports
  • SPEC Releases
  • DEMO Vue application demo
  • SPEC Complete UI theme specification