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. - Pages use THE STANDARD SHELL (lister.md "Page markup" — the card below, whole on the data list): the toolbar inside
.lister-main(the Filters toggle, icon + word, + the bulk Actions left; the single actions right — no toolbar Select all), the panel as two cards (Filters: title + thelister-resetReset link + the close button, the search, the filters; Display: order + page size), the list card (.lister-list-card: the header, its select-all checkbox first, + the rows), the count + the pager on one.lister-footerline, the details as.lister-more-inset. The minimal instances here keep only the parts they use. - Rows use THE STANDARD ROW (lister.md "The standard row"; whole on the data list): the card is the details toggle (
rowClick: 'expand'+renderMore+lister-more-inset), "Details" markedlister-expandrides first in the row's dropdown — or stands as the bare.btn-sm .btn-actions-contrastbutton when it is the row's only action (the shell card below). No caret at the row end;renderMorelays its fields out as therow g-3 smallgrid, label.text-body-secondaryover value. - 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 with FIT columns (lister.md "Fit columns"; the full rules: data list): each cell in
.lister-colsdeclares its share and readable minimum (lister-col-w="40" lister-col-min="9rem"), each one that may hide alister-col-priority+lister-col="Label"— the lib hides them by the LIST's width (open the filter panel below) and shows them at the top of the open details; the identity (the name) never hides. Now-*/d-none d-*-blockclasses on the cells. Row buttons live in the fixed-width.lister-actionscolumn, present on every row. - The header row
.lister-headeris the first row of the list card: ONE.lister-colswith plain column labels over the cells, in the rows' order (no width classes — the lib sizes the labels like the rows' FIT cells and hides a label with its column), hidden below the stacking breakpoint (d-none d-sm-flex); withselection: 'multiple'the select-all checkbox (.lister-check) comes first. Column-like rows get one; free-form rows (the checklist above) go without. 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. Side info shown to the user (pending invitations, side totals) has a standardized home: the NOTICE STRIP,[lister-notice]above the rows (tabbed data shows it live). - 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.
The standard shell — what every list PAGE uses (lister.md "Page markup")
Name
Email
Role