lister

Data lists — list, grid, compact; states and keys for free

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-cols sized with the w-* utilities so rows align like a table; row buttons live in the fixed-width .lister-actions column, present on every row.
  • The header row .lister-header (the minimal list above) is OPTIONAL: ONE .lister-cols with column labels over the cells, same w-* 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-actions inside 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) => rows extracts the rows, and lister:loaded carries the raw data for 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 emits lister:reorder with the new id order — SAVING is the page's job (endpoint, then reload()). Mouse-only; pair with paging: 'none'.
  • Docs: templates/app/docs/libs/lister.md — the attribute/option/event reference.