Minimal list — attribute config, only the render callback in JS
Name
Email
Role
Reorder & atypical data — map + reorder
Minimal grid — init options
The rules
- ANY tabular data — any list of records — is a lister with card rows; this is THE house way to show data (real
.tables are the rare small-summary exception, tables). Never a hand-rolled fetch+render loop. One element (.lister+ the slots) and one call:g.lister(el, { endpoint, render }). - Config lives in
lister-*ATTRIBUTES on the root (the minimal list above: endpoint, per-page, keys off) or in init options — attributes win; the render callback is the one thing that stays JS. - Views: list (rows — the default; the full page: data list) and grid (cards; the full page: data grid). The view is a PAGE decision, never a user toggle.
- List rows follow the column standard (the full rules: data list): cells in
.lister-colssized with thew-*utilities so rows align like a table; row buttons live in the fixed-width.lister-actionscolumn, present on every row. - The header row
.lister-header(the minimal list above) is OPTIONAL: ONE.lister-colswith column labels over the cells, samew-*classes, hidden below the stacking breakpoint (d-none d-sm-flex). PREFERABLE on compact listers — their cells carry no inline labels — but never required: full rows label themselves. When the list has an actions column, the header reserves its width AUTOMATICALLY from--lister-actions-w— never author a.lister-actionsinside the header (it would reserve twice; without the reservation the last label would right-align over the buttons while its column stops before them). - The render callback returns a template literal — escape EVERY interpolation with
h.esc. - Skeletons, empty, error+retry, paging, selection, URL sync and keyboard come FROM the lib — configure, never rebuild (states).
- An ATYPICAL payload (rows under a key, side data riding along) is no reason to skip the lister:
map: (data) => rowsextracts the rows, andlister:loadedcarries the rawdatafor the extras. The lister standardizes the LOOK — it never constrains your data. - Orderable lists declare
reorder({ handle, group }): the lib drags rows within their group and emitslister:reorderwith the new id order — SAVING is the page's job (endpoint, thenreload()). Mouse-only; pair withpaging: 'none'. - Docs:
templates/app/docs/libs/lister.md— the attribute/option/event reference.