UI elements

Buttons, badges, alerts, toasts, icons — the vocabulary

Buttons — hierarchy first, color second

Hierarchy — ONE primary per zone, solid secondary for everything else
Sizes — default in headers/forms, .btn-sm inside cards and table rows; .btn-lg is not used
Icon buttons — icon-only ALWAYS carries title (auto-tooltip); with a label the icon takes .me-1
Row actions — the three shapes + the round expand toggle (live on the data-list page); .btn-contrast adapts to the theme
Destructive — outline in rows/cards; solid .btn-danger only as a dialog's confirm button
  • ONE .btn-primary per zone (page header, card, modal footer) — if two things feel primary, one of them is not.
  • The default secondary is the SOLID .btn-secondary (steel blue) — the outline variants are not part of the standard hierarchy (they live in the catalog; .btn-outline-danger keeps its role on STANDALONE destructive row actions).
  • Outline buttons NEVER sit inside a .btn-group — the shared internal borders read badly. Grouped buttons are SOLID (.btn-secondary, .btn-danger, .btn-contrast); a segmented btn-check choice uses .btn-soft-secondary.
  • Destructive actions always go through a dialog confirm; the confirm button inside the dialog is the solid .btn-danger.
  • Cancel is .btn-soft-outline-secondary and sits at the LEFT edge of the button row; the action buttons sit RIGHT-aligned, primary last. The row lives in the .card-footer / .modal-footer whenever the form owns one (forms) — any other arrangement is custom, outside the standard.
  • .btn-contrast is the theme-adaptive contrast button (dark on the light theme, light on the dark theme, same class): the standard for utility row controls — the kebab actions button, the split-dropdown pair, the round expand toggle.
  • Row/table actions come in THREE shapes — pick ONE per list and stay consistent: the inline .btn-group of .btn-sm icon buttons; the split dropdown (primary action + .dropdown-toggle-split caret, both .btn-contrast); the actions-only kebab (.btn-contrast + ri-more-2-line opening all actions). The expand toggle is never part of the group: a small round .btn-contrast at the row end (.btn-xs .btn-icon-round — the circle sizes from the size class).
  • Dropdown toggles NEVER carry title (the auto-tooltip and the dropdown are two Bootstrap instances on one element — Bootstrap allows only one); label them with a .visually-hidden span.
  • Buttons in a row sit in a flex container with gap-2 — no spacing utilities on the buttons themselves.

Alerts — persistent in-page notices, used sparingly

  • An alert is a persistent state the user must know about while on the page (trial ending, unpaid invoice, config missing) — not feedback for an action they just did (that is a toast).
  • Variants used: alert-info and alert-warning for notices, alert-danger for blocking problems; alert-success is almost never right (success is a toast).
  • Validation errors are NEVER alerts — the validator renders them inline at the fields.

Badges — statuses on the subtle/emphasis pair

Active Pending Blocked Trial Archived
  • A status badge is ALWAYS the bg-*-subtle text-*-emphasis pair — solid bg-* badges are not used for statuses (they shout and break in dark mode).
  • Semantics, fixed app-wide: success = active/ok, warning = transitional/attention, danger = blocked/failed, info = informational, secondary = neutral/inactive.
  • Same status = same color everywhere in the app — define the mapping once and stick to it.

Toasts — feedback for actions (live demo)

  • A toast answers "did my action work?": g.success after a save/delete, g.error for system failures, g.warning for business refusals (res.error.code ≥ 1000).
  • After a redirect/reload, pass { nextPage: true } and the toast shows on the page you land on.
  • Toasts never carry state the user must not miss — that is an alert on the page.
  • Full API and options: templates/app/docs/libs/genus.md "Toasts".

Icons — Remix Icon, line variants

  • The icon set is Remix Icon; the -line variants are the default everywhere (-fill only when a filled state means something, e.g. a toggled favorite).
  • Icons are decoration: aria-hidden="true" always; the meaning lives in the label or the title.
  • Keep one icon per concept app-wide (delete is always ri-delete-bin-line, edit is always ri-pencil-line).