Welcome to Vanilla Breeze
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
Client-side list/table sorting upscale on any container — native ul/ol/table or VB collections. Two API shapes (clickable column headers OR external select/button); composes with data-paged.
The data-sortable attribute upscales any list-shaped container with client-side sorting. Same upscale model as data-paged: pure attribute, no custom element, auto-attaches via the shared init registry. Two API shapes share one engine — clickable <th> headers for tables, external controls (<select> or <button>) for other containers.
Clicking <th data-sort="key"> cycles the column through asc → desc → none → asc. Row sort-value comes from the matching <td> via (in order): data-value, <time datetime> when type is date, otherwise textContent.
<table data-sortable> <thead> <tr> <th data-sort="name">Name</th> <th data-sort="score" data-sort-type="number">Score</th> <th data-sort="joined" data-sort-type="date">Joined</th> </tr> </thead> <tbody> <tr><td>Alice</td><td>87</td><td><time datetime="2026-03-14">Mar 14</time></td></tr> ... </tbody></table>
Items declare data-sort-{key}=value attributes; an external control declares data-sort-target=selector and supplies the active key. A <select>'s value (or a <button>'s data-sort-by attribute) chooses the key. Leading - on the value flips direction.
<select data-sort-target="#projects"> <option value="name">Name (A-Z)</option> <option value="-name">Name (Z-A)</option> <option value="-priority">Priority (high to low)</option> <option value="due">Due date (soonest)</option></select> <ul id="projects" data-sortable> <li data-sort-name="Bonsai" data-sort-priority="2" data-sort-due="2026-08-12">...</li></ul>
| Attribute | Default | Notes |
|---|---|---|
data-sortable | — | Opt-in (boolean) on the container. |
data-sortable-default | — | Initial sort key (e.g. -date for newest-first). |
data-sort-by | — | Reflected on the host: current sort key. |
data-sort-direction | — | Reflected on the host: asc · desc · none |
data-sort | — | On <th> in table mode: column key. |
data-sort-type | text | text · number · date — on container or <th>. |
data-sort-target | — | On external controls: CSS selector for the target container. |
data-sort-by | — | On a <button> control: the key to apply on click. |
data-sort-{key} | — | On list children: per-key sort value. |
data-value | — | Per-cell override; takes precedence over textContent. |
The container emits sort:change after each reorder. Use it for analytics, scroll-restoration, or driving a sibling component.
list.addEventListener('sort:change', (e) => { const { key, direction, total } = e.detail;});
localeCompare with sensitivity: "base" and numeric: true. Case-insensitive; "item 10" sorts after "item 2".Number() coercion; non-numeric values sort to the end.Date.parse. Use ISO 8601 (YYYY-MM-DD) or <time datetime> for unambiguous parsing.Empty / nullish values always sort to the end regardless of direction — "no value" is treated as "no opinion, put it last."
role="button", tabindex="0", and reflected aria-sort (ascending / descending / none).<select> / <button> — native a11y.data-pagedDrop both attributes on the same container. Sorting reorders children; the data-paged engine's MutationObserver sees the reorder and re-paginates from page 1, so the user always lands on the first page of the newly sorted list.
<ul id="feed" data-sortable data-paged data-paged-size="20"> <li data-sort-name="..." data-sort-date="...">...</li> ...</ul>
data-table instead<data-table> ships its own column-level data-sort alongside data-weight, data-rollup, data-heatmap, etc. If you're already using <data-table> for typed cells / weighted rollups / heatmaps, use its built-in sort. Reach for data-sortable when:
<ul>, <ol>, <card-list>, <layout-grid>).<table> and don't want the <data-table> upgrade surface.