Datagrid prefs
Column preferences, persisted the hypermedia way — two wires, zero new JavaScript beyond what already ships:
- Widths —
installDatagrid()mirrors every committed resize into anyinput[data-hc-datagrid-width="<col>"]before dispatchinghc:datagridcolumnresize; a small form autosaves it on the debounced event. The server persists per user and renders the remembered widths back as inline widths +data-resizedon later loads — the width is a server-rendered fact. - Order — the
datagrid-columns
chooser upgraded with
installSortable(): checkbox serialization follows DOM order, and the server honors the submittedcols=sequence as the column order.
Covered by the versioning policy.
Live demo
Section titled “Live demo”Drag the Name or Status resize grip (or focus it and press the arrow keys). The committed width lands in the hidden inputs, the debounced form posts them, and the status line answers with what a real server would persist:
| Name | Status | Owner |
|---|---|---|
| Ingest pipeline | Active | Ada |
| Billing export | Pending | Alan |
The markup — widths
Section titled “The markup — widths”<!-- In the grid: resizable headers must name their column — installDatagrid() skips a data-resizable header without data-col. --><th class="hc-datagrid__headcell" data-resizable data-col="name" scope="col">Name</th><th class="hc-datagrid__headcell" data-resizable data-col="status" scope="col">Status</th>
<form action="/prefs/columns" method="post" data-hx-post="/prefs/columns" data-hx-trigger="hc:datagridcolumnresize from:body delay:500ms" data-hx-include="this" data-hx-target="#prefs-status" data-hx-swap="innerHTML"> <input type="hidden" name="w-name" data-hc-datagrid-width="name"> <input type="hidden" name="w-status" data-hc-datagrid-width="status"> <div id="prefs-status" role="status" class="hc-sr-only"></div></form>The mirror happens before the event dispatch, so the
event-triggered request always serializes the fresh value. Mirrored
inputs are found in the grid’s closest <form>, else document-wide.
The markup — order
Section titled “The markup — order”<fieldset class="hc-popover__body" id="cols-fields" data-hc-sortable> <div class="hc-item"> <button type="button" class="hc-button" data-variant="ghost" data-hc-sortable-handle aria-label="Reorder Name">⠿</button> <label class="hc-checkbox-label"> <input class="hc-checkbox" type="checkbox" name="cols" value="name" checked> Name </label> </div> <!-- …one sortable row per column… --></fieldset>Each row is a direct child of the sortable fieldset, and the handle
sits beside the label, never inside it — interactive content
inside a <label> is invalid HTML, and clicking the handle would
toggle the checkbox. Drag (or grab with the keyboard) to reorder —
Apply serializes the
checkboxes in DOM order, and the upgraded
datagrid-columns contract
renders the columns in that sequence.
Server response contract
Section titled “Server response contract”POST /prefs/columns (w-<col> pairs):
| Case | Response (200) |
|---|---|
any w-<col> set | the role="status" fragment (e.g. “Saved — Name 220px”); persisted per user |
| invalid value | clamped or ignored server-side, still 200 — a width is a preference, never an error |
| no-JS | native POST → 303 PRG |
Rendering back is the other half: on later loads the server renders
remembered widths as
style="inline-size: <w>px; max-inline-size: <w>px" + data-resized
on the column’s header and cells — print/export and other sessions
agree with the screen.
Progressive enhancement
Section titled “Progressive enhancement”Without JavaScript there is no client resize — and the server-rendered widths still apply. The order chooser stays a real GET form; without the sortable upgrade the checkboxes still choose the set.
Accessibility
Section titled “Accessibility”- Saves announce through the
role="status"region — never silent. - Sortable handles are real buttons with
installSortable()’s keyboard grammar (Space/Enter grabs, arrows move, Escape cancels). - Column resize itself is keyboard-accessible (the grip is a focusable separator) — so the whole prefs loop is.
Related
Section titled “Related”- datagrid-columns — the chooser this recipe upgrades with ordering.
- sortable — the reorder behavior and its keyboard grammar.
- autosave — the same debounced-persistence idea for whole forms.
- Datagrid — column resize and the mirrored-input hook.