Reference · SemText · Data & tables
<sem-table>
SemText element
You write a normal HTML table. Sorting and filtering only change what the reader sees; the markup stays the contract, so machines always read the same rows you wrote.
Children
-
<table>Child: **exactly one `<table>`**, holding exactly one thead (its first row: header cells carrying `scope="col"`), one tbody, and optionally a caption first. A table without a thead gets no sort control. Anything else inside the table — tfoot, colgroup — is left alone.
Example
Live render boots with JavaScript — the authored source is alongside.
variant: filter without sort
`controls="filter"` drops the sort buttons: the chrome carries the labelled search box and the live status only, and the headers render as plain header cells. Typing hides every row whose text does not match (native `hidden`, so the rows leave the accessibility tree with the visual) and announces "n of m rows" about 200 ms after typing settles — not on every keystroke.
Live render boots with JavaScript — the authored source is alongside.
variant: no attributes at all — sort is the default
Bare `<sem-table>`: `controls` defaults to `sort`, and with no filter input and an empty status the chrome hides itself, so the reader sees only the table. Activating Count sorts numerically (3 < 9 < 14) even though the cells are plain text — a column of pure numbers is compared as numbers; a third activation restores the authored order.
Live render boots with JavaScript — the authored source is alongside.
variant: sticky over a long body
`sticky` caps the element at 24rem so a long table scrolls inside itself and the header sticks while it scrolls — a stick needs an internal scroll container to engage; without the cap a page-level table never gains one and the header would scroll away with the page. CSS only: the header still sticks with scripts off. Sort by When — ties keep the authored order.
Live render boots with JavaScript — the authored source is alongside.
variant: kind="comparison" — first column pinned on narrow screens
`kind="comparison"` (on the element or on a plain `<table>`) pins the first column while the remaining columns scroll horizontally — shrink the window (or read at phone width) and the plan names stay in place against the columns that slide. CSS only, works with scripts off; with `sticky` it also holds while the header is stuck.
Live render boots with JavaScript — the authored source is alongside.
variant: data-value carries a hidden sort key
`td[data-value]` is the sort key when the visible text is not it: Shipped sorts chronologically (Jan, Feb, Mar) though the cells read "12 Jan 2026" …, and Release sorts "v2.10" after "v2.9" because text comparison is locale-aware with numeric runs. Two numeric keys compare numerically; anything else compares as text.
Live render boots with JavaScript — the authored source is alongside.
variant: no thead — the plain-table floor
A table without a thead has no header row to sort by: no sort buttons are wired, the chrome stays empty and hides itself, and what remains is the authored table exactly as written. This is the element's floor — it degrades to plain HTML, never to a broken control.
Live render boots with JavaScript — the authored source is alongside.
Source: spec/schema/sem-table.md