j3w1 / Filterable tableVue · HTML · React · Astro

J3W1 / DESIGN SYSTEM

Filterable table

A list screen composed from a toolbar with search and filter, removable chips, a selectable sortable data-table with status badges, a count and pagination; with empty and loading variants.

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-filterable-table id="example-filterable-table-default"><section class="filterable-table" aria-label="Project records"><div class="filterable-table-toolbar"><j3w1-search-field><div class="search-field"><label class="search-field-label" for="example-filterable-table-default-records-search">Search projects</label><div class="search-field-root"><input class="search-field-input" id="example-filterable-table-default-records-search" type="search"><button class="search-field-clear" type="button" aria-label="Clear search">Clear</button></div></div></j3w1-search-field><label for="example-filterable-table-default-records-status">Status<select class="select-control" id="example-filterable-table-default-records-status" data-status-filter="" name="example-filterable-table-default-records-status"><option value="all">All statuses</option><option>Ready</option><option>In progress</option><option>Review</option></select></label></div><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-filterable-table-default-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-filterable-table-default-record-name-0" name="example-filterable-table-default-value"></td><td class="data-table-cell" id="example-filterable-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-filterable-table-default-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-filterable-table-default-record-name-1" name="example-filterable-table-default-value"></td><td class="data-table-cell" id="example-filterable-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-filterable-table-default-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-filterable-table-default-record-name-2" name="example-filterable-table-default-value"></td><td class="data-table-cell" id="example-filterable-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-filterable-table-default-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-filterable-table-default-record-name-3" name="example-filterable-table-default-value"></td><td class="data-table-cell" id="example-filterable-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="5">No rows match.</td></tr><tr class="data-table-loading" hidden=""><td colspan="5">⋯ Loading rows</td></tr></tbody></table></div><p class="filterable-table-count" role="status" data-table-status=""></p></section></j3w1-filterable-table>

empty

Project records · synthetic fixture
No rows match.

<j3w1-filterable-table id="example-filterable-table-empty"><section class="filterable-table" aria-label="Project records"><div class="filterable-table-toolbar"><j3w1-search-field><div class="search-field"><label class="search-field-label" for="example-filterable-table-empty-records-search">Search projects</label><div class="search-field-root"><input class="search-field-input" id="example-filterable-table-empty-records-search" type="search"><button class="search-field-clear" type="button" aria-label="Clear search">Clear</button></div></div></j3w1-search-field><label for="example-filterable-table-empty-records-status">Status<select class="select-control" id="example-filterable-table-empty-records-status" data-status-filter="" name="example-filterable-table-empty-records-status"><option value="all">All statuses</option><option>Ready</option><option>In progress</option><option>Review</option></select></label></div><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-empty"><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><p class="filterable-table-count" role="status" data-table-status=""></p></section></j3w1-filterable-table>

loading

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

<j3w1-filterable-table id="example-filterable-table-loading"><section class="filterable-table" aria-label="Project records"><div class="filterable-table-toolbar"><j3w1-search-field><div class="search-field"><label class="search-field-label" for="example-filterable-table-loading-records-search">Search projects</label><div class="search-field-root"><input class="search-field-input" id="example-filterable-table-loading-records-search" type="search"><button class="search-field-clear" type="button" aria-label="Clear search">Clear</button></div></div></j3w1-search-field><label for="example-filterable-table-loading-records-status">Status<select class="select-control" id="example-filterable-table-loading-records-status" data-status-filter="" name="example-filterable-table-loading-records-status"><option value="all">All statuses</option><option>Ready</option><option>In progress</option><option>Review</option></select></label></div><div class="table-scroller" tabindex="0" role="region" aria-label="Project records"><table class="data-table" aria-busy="true"><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-filterable-table-loading-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-filterable-table-loading-record-name-0" name="example-filterable-table-loading-value"></td><td class="data-table-cell" id="example-filterable-table-loading-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-filterable-table-loading-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-filterable-table-loading-record-name-1" name="example-filterable-table-loading-value"></td><td class="data-table-cell" id="example-filterable-table-loading-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-filterable-table-loading-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-filterable-table-loading-record-name-2" name="example-filterable-table-loading-value"></td><td class="data-table-cell" id="example-filterable-table-loading-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-filterable-table-loading-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-filterable-table-loading-record-name-3" name="example-filterable-table-loading-value"></td><td class="data-table-cell" id="example-filterable-table-loading-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"><td colspan="5">⋯ Loading rows</td></tr></tbody></table></div><p class="filterable-table-count" role="status" data-table-status=""></p></section></j3w1-filterable-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/filterable-table.css';
import '@j3w1/ui/register/filterable-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 filterable-table --out ./vendor/j3w1/filterable-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: search-field. Style dependencies: data-table, menu, select, table, search-field.

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 list screen that proves the navigation, display and feedback components compose around one data-table: find, filter, select, page. It exists to show the states of a whole list screen, including nothing matching and still loading, not to prescribe a product.

Anatomy

The region landmark; the toolbar on color.surface.chrome with the search-field, the Filter menu button, the Columns toggle, a separator and the primary New button; a row of removable chips (priority: R1, maturity: stable); the scroller around the data-table, whose Name header carries aria-sort, whose rows carry aria-selected and a checkbox, and whose last column is a status badge (✓ Tested on color.status.success.fill, i Demonstrated on color.status.info.fill); the count in color.text.muted and the compact pagination. The empty variant adds the empty-state block; the loading variant marks the table aria-busy. Every part is the component’s own markup and classes.

States

StateVisualNon-colour channel
defaultrest tokens of every component; Name sorted ascending with its ▲ glyph; Prev disabledaria-sort; the glyph
hoverthe pointed row takes color.interaction.hover.bg; the toolbar controls and chips keep their own hover on their own pointer events onlycursor
focus-visiblethe dashed color.interaction.focus.ring on the row holding focus (shown on the third row); the toolbar and chips stay at restthe ring
selectedtwo rows take color.interaction.selection.bg with color.interaction.selection.text and a checked box; the select-all reports mixed with the – glypharia-selected; ✓ and – glyphs; aria-checked="mixed"
no-resultsthe body is the “No components match” row, the count reads 0 of 43, the search field’s status line announces it; the chips stay so the cause is visibletext; role="status"

The composed components keep their own pressed, sorted, disabled, loading and open treatments; this specimen declares only the states the whole screen adds to them.

Keyboard

Landmarks first: the region, the toolbar’s search landmark and the pagination navigation are offered by name. The toolbar is one stop with arrow movement between the search field, Filter, Columns and New (the toolbar contract); Escape clears the search. Each chip’s Remove button is its own stop. The table follows the data-table contract: the first row is the stop, arrows move between rows, Space on a row’s checkbox toggles it, Enter on a header sorts. Next is the last stop; a disabled Prev is not focusable.

Accessibility

The search field’s visible label is replaced by a visually hidden one so the toolbar stays one row; its status line is visually hidden too and announces “No components match” through role="status". The count is a second status region and changes once per filter. Rows are named by their Name cell through aria-labelledby on the row checkbox; the select-all is a checkbox-role button whose mixed state carries the – glyph. Badges carry their glyphs so status survives without colour. Contrast: toolbar text 8.60:1 on the chrome surface, boundaries 4.42:1, headers 10.10:1, badges 7.08:1 and 7.13:1 on their fills, the ring 7.48:1 on the selection fill.

Portability

Native table semantics, a toolbar, a menu button and a status region; the roving tabindex and the menu need the host’s script, every state renders without one from the root hooks data-state-selected and data-state-no-results and the table’s own aria-busy. Hosts without container queries wrap the toolbar at the 600px viewport breakpoint. The 56rem width and the 44rem table minimum are layout of this specimen and not rules; the specimen proves composition, not a product.

Non-examples

A rounded search pill with a magnifier inside a shadowed toolbar. Filter chips drawn as pills with a coloured fill per category. Zebra-striped rows. A selected row shown by a coloured left bar and no fill. A blue Material checkbox. A skeleton shimmer while loading. An infinite scroll with no count and no pages.

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