Scoria's shared dashboard component vocabulary.
Function components emit the brand-book semantic classes (see assets/css/04-components.css)
driven entirely by design tokens. This is the single home for tone/status → color mapping,
replacing the per-component badge_class/status_color/trace_badge_class/flash_kind_class
helpers that previously drifted across the codebase.
Import into a LiveView/component with import ScoriaWeb.UI.
Summary
Functions
Status Home attention strip card for nonzero actionable states.
Renders as an <a> tag — callers pass a path, not a click handler.
Attrs: count (numeric highlight), label (category name), detail
(supporting copy), cta (call-to-action text), path (link target),
tone (semantic tone atom controlling color treatment, default :neutral).
Status badge. Always renders a text label alongside color (a11y: never color-alone).
Primary/ghost/danger button (brand book §8.5).
Server-rendered command palette shell. Client-side filtering lives in assets/js/scoria.js.
Slot-based drawer panel shell (DS-02). Renders nothing when show=false. When show=true, renders a scrim + aside panel with a consistent triple dismiss contract: Close drawer button + scrim click + Escape key. The caller owns all dismiss events via on_dismiss.
Empty state: status + learning cue + optional primary action (NN/g).
Compact evidence action/link row. Callers own the action/link semantics.
Used for per-section action links in evidence panels (e.g. "View trace",
"Open replay", "Go to approval"). Renders :inner_block inside a flex
container with consistent action-row spacing — place <a> or <.button>
elements inside.
Notebook-scoped evidence empty state. Used for empty :tab slot panels
in <.notebook> when no evidence data is available for a section. The
:title attr is required and names what is absent (e.g. "No approvals",
"No connector invocations"). Optional :inner_block can provide
supplementary copy or a link.
Stable key-value evidence rows for adapter-projected values.
Renders a <dl> of <dt>/<dd> pairs. The :rows attr accepts a list of
either %{label: _, value: _} maps or {label, value} two-tuples — both
forms are normalized by normalize_evidence_rows/1 before rendering.
Use inside <.evidence_section> :inner_block for structured label-value
evidence data (e.g. model name, token count, latency).
Notebook-scoped evidence section with optional status badge and action slot.
Used inside <.notebook> :tab slot panels to group a titled block of
evidence content. Attrs: title (section heading), description (optional
supporting copy), tone (semantic tone atom for the badge color),
badge (optional badge label — rendered only when present).
Slots: :actions for action buttons rendered in the section header;
:inner_block (required) for evidence rows and action rows.
Small uppercase category/status label (brand book card eyebrow). Used in panel headers, object headers, and card hierarchy labeling to provide a typographic tier above the primary title.
Form field wrapper (DS-03). Renders a label + caller-provided input slot + optional help text + validation error. Error is surfaced via an exclamation icon + text (never error by color alone). Required fields include an aria-hidden asterisk and a visually-hidden '(required)' span. The caller provides the actual input/select/textarea element via the inner_block slot.
Dashboard flash banners. Single home for flash kind → tone styling (DS-05). Renders semantic scoria-flash--{tone} BEM modifier classes via string-keyed clauses (Phoenix @flash always provides string keys, not atoms). Each banner carries role="alert" and a 16×16 tone icon so status is never communicated by color alone.
Form section group (DS-03). Groups related <.field> components under a section heading with an optional description.
Copyable monospace identifier (run/trace/actor IDs). Uses the CopyId JS hook.
Keyboard shortcut chip. Renders a <kbd> element styled with the
brand-book monospace treatment. Used inline in command palette rows and
help text to label key bindings (e.g. ⌘K, Escape, ↑↓).
Metric card: label, big value, explicit delta (brand book §11.3 — never a magic score).
Slot-based modal dialog shell (DS-02). Renders nothing when show=false. When show=true, renders a scrim + panel with a consistent triple dismiss contract: close button + scrim click + Escape key. The caller owns all dismiss events via on_dismiss.
Unified tabbed evidence panel shell (DS-04). Renders a <nav role='tablist'> tab bar with one <button role='tab'> per <:tab> slot. The tab matching @selected_tab carries aria-selected='true'. Clicking a tab emits phx-click={@on_tab_change} phx-value-tab={key}. When @empty is true, the :empty_slot body is rendered instead of the tab bar and panel.
Object-page orientation header: parent crumb, copyable ID, status, provenance, and return context.
Panel/card surface with optional eyebrow + title + actions header.
Raw evidence details/pre block (DS-04). Renders a <details>/<summary> with a <pre> code block. Background and font tokens are applied via CSS class — not overridden in HEEx.
Loading skeleton placeholder (DS-05). Renders stacked skeleton lines with aria-label='Loading…' and role='status'. Pulse animation + prefers-reduced-motion suppression are handled by CSS.
Human label for a status string (title-cased, underscores → spaces).
Honest reserved-capability stub page. Future-tense only; no fake rows or charts.
Sortable, density-aware, paginated data table (DS-01). Renders column headers from typed <:col> slots; emits sort events for keyed columns only when on_sort is supplied by the parent LiveView. Uses density modifier classes from density_class/1. Falls back to a default empty state when rows is empty (overridable via <:empty>). Pagination strip shown when total_pages > 1. Density controls render only when on_density_change is supplied by the parent LiveView.
Transient toast notification (DS-05). Driven by a server @toasts assign. Auto-dismisses via phx-mounted={JS.hide(...)}. Manual dismiss X button is also provided. Does NOT use a JS hook (untestable in LiveViewTest). The phx-mounted auto-dismiss omits 'to:' so it self-targets the toast div (Pitfall 3); the dismiss button MUST target the toast by id (to: '#id') — a bare JS.hide there would hide the button, not the toast.
Maps a domain status/kind string (or atom) to a semantic tone atom.
Functions
Status Home attention strip card for nonzero actionable states.
Renders as an <a> tag — callers pass a path, not a click handler.
Attrs: count (numeric highlight), label (category name), detail
(supporting copy), cta (call-to-action text), path (link target),
tone (semantic tone atom controlling color treatment, default :neutral).
Attributes
count(:any) (required)label(:string) (required)detail(:string) (required)cta(:string) (required)path(:string) (required)tone(:atom) - Defaults to:neutral.class(:string) - Defaults tonil.
Status badge. Always renders a text label alongside color (a11y: never color-alone).
Attributes
tone(:atom) - Defaults to:neutral.label(:string) - Defaults tonil.dot(:boolean) - Defaults totrue.class(:string) - Defaults tonil.- Global attributes are accepted.
Slots
inner_block
Primary/ghost/danger button (brand book §8.5).
Attributes
variant(:atom) - Defaults to:primary. Must be one of:primary,:ghost, or:danger.size(:atom) - Defaults to:md. Must be one of:md, or:sm.type(:string) - Defaults to"button".class(:string) - Defaults tonil.- Global attributes are accepted. Supports all globals plus:
["phx-click", "phx-target", "phx-value-id", "phx-value-approval-id", "phx-value-dataset-id", "phx-disable-with", "disabled", "form", "name", "value", "href", "aria-current"].
Slots
inner_block(required)
Server-rendered command palette shell. Client-side filtering lives in assets/js/scoria.js.
Attributes
id(:string) (required)sections(:list) - Defaults to[].title(:string) - Defaults to"Open command palette".placeholder(:string) - Defaults to"Search screens, recent objects, and actions".empty_copy(:string) - Defaults to"No matches. The palette covers screens, recent objects, and actions — full object search lands in a later release.".class(:string) - Defaults tonil.
Slot-based drawer panel shell (DS-02). Renders nothing when show=false. When show=true, renders a scrim + aside panel with a consistent triple dismiss contract: Close drawer button + scrim click + Escape key. The caller owns all dismiss events via on_dismiss.
Attributes
id(:string) (required)show(:boolean) (required)on_dismiss(:string) (required)title(:string) - Defaults tonil.- Global attributes are accepted.
Slots
eyebrowtitle_slotactionsinner_block(required)
Empty state: status + learning cue + optional primary action (NN/g).
Attributes
title(:string) (required)class(:string) - Defaults tonil.
Slots
inner_blockaction
Compact evidence action/link row. Callers own the action/link semantics.
Used for per-section action links in evidence panels (e.g. "View trace",
"Open replay", "Go to approval"). Renders :inner_block inside a flex
container with consistent action-row spacing — place <a> or <.button>
elements inside.
Attributes
class(:string) - Defaults tonil.- Global attributes are accepted.
Slots
inner_block(required)
Notebook-scoped evidence empty state. Used for empty :tab slot panels
in <.notebook> when no evidence data is available for a section. The
:title attr is required and names what is absent (e.g. "No approvals",
"No connector invocations"). Optional :inner_block can provide
supplementary copy or a link.
Attributes
title(:string) (required)class(:string) - Defaults tonil.- Global attributes are accepted.
Slots
inner_block(required)
Stable key-value evidence rows for adapter-projected values.
Renders a <dl> of <dt>/<dd> pairs. The :rows attr accepts a list of
either %{label: _, value: _} maps or {label, value} two-tuples — both
forms are normalized by normalize_evidence_rows/1 before rendering.
Use inside <.evidence_section> :inner_block for structured label-value
evidence data (e.g. model name, token count, latency).
Attributes
rows(:list) (required)class(:string) - Defaults tonil.- Global attributes are accepted.
Notebook-scoped evidence section with optional status badge and action slot.
Used inside <.notebook> :tab slot panels to group a titled block of
evidence content. Attrs: title (section heading), description (optional
supporting copy), tone (semantic tone atom for the badge color),
badge (optional badge label — rendered only when present).
Slots: :actions for action buttons rendered in the section header;
:inner_block (required) for evidence rows and action rows.
Attributes
title(:string) (required)description(:string) - Defaults tonil.tone(:atom) - Defaults to:neutral. Must be one of:pass,:info,:warn,:fail,:trace,:brand, or:neutral.badge(:string) - Defaults tonil.class(:string) - Defaults tonil.- Global attributes are accepted.
Slots
actionsinner_block(required)
Small uppercase category/status label (brand book card eyebrow). Used in panel headers, object headers, and card hierarchy labeling to provide a typographic tier above the primary title.
Attributes
class(:string) - Defaults tonil.
Slots
inner_block(required)
Form field wrapper (DS-03). Renders a label + caller-provided input slot + optional help text + validation error. Error is surfaced via an exclamation icon + text (never error by color alone). Required fields include an aria-hidden asterisk and a visually-hidden '(required)' span. The caller provides the actual input/select/textarea element via the inner_block slot.
Attributes
id(:string) (required)label(:string) (required)help(:string) - Defaults tonil.error(:string) - Defaults tonil.required(:boolean) - Defaults tofalse.- Global attributes are accepted.
Slots
inner_block(required)
Dashboard flash banners. Single home for flash kind → tone styling (DS-05). Renders semantic scoria-flash--{tone} BEM modifier classes via string-keyed clauses (Phoenix @flash always provides string keys, not atoms). Each banner carries role="alert" and a 16×16 tone icon so status is never communicated by color alone.
Attributes
flash(:map) - Defaults to%{}.
Form section group (DS-03). Groups related <.field> components under a section heading with an optional description.
Attributes
title(:string) (required)description(:string) - Defaults tonil.- Global attributes are accepted.
Slots
inner_block(required)
Copyable monospace identifier (run/trace/actor IDs). Uses the CopyId JS hook.
The DOM id must be stable across renders so LiveView/morphdom patches the existing element in place rather than tearing down and re-mounting the CopyId hook. When no caller-supplied id is given it is derived deterministically from the displayed value; prefer passing an explicit id where two identical values can appear on one page.
Attributes
value(:string) (required)copy(:string) - Defaults tonil.id(:string) - Defaults tonil.title(:string) - Defaults to"Click to copy".class(:string) - Defaults tonil.
Keyboard shortcut chip. Renders a <kbd> element styled with the
brand-book monospace treatment. Used inline in command palette rows and
help text to label key bindings (e.g. ⌘K, Escape, ↑↓).
Attributes
class(:string) - Defaults tonil.
Slots
inner_block(required)
Metric card: label, big value, explicit delta (brand book §11.3 — never a magic score).
Attributes
label(:string) (required)value(:string) (required)delta(:string) - Defaults tonil.delta_tone(:atom) - Defaults to:neutral.class(:string) - Defaults tonil.
Slot-based modal dialog shell (DS-02). Renders nothing when show=false. When show=true, renders a scrim + panel with a consistent triple dismiss contract: close button + scrim click + Escape key. The caller owns all dismiss events via on_dismiss.
Attributes
id(:string) (required)show(:boolean) (required)on_dismiss(:string) (required)title(:string) - Defaults tonil.max_width(:string) - Defaults to"560px".- Global attributes are accepted.
Slots
title_slotinner_block(required)footer
Unified tabbed evidence panel shell (DS-04). Renders a <nav role='tablist'> tab bar with one <button role='tab'> per <:tab> slot. The tab matching @selected_tab carries aria-selected='true'. Clicking a tab emits phx-click={@on_tab_change} phx-value-tab={key}. When @empty is true, the :empty_slot body is rendered instead of the tab bar and panel.
Attributes
id(:string) (required)title(:string) (required)eyebrow(:string) - Defaults tonil.empty(:boolean) - Defaults tofalse.selected_tab(:string) - Defaults tonil.on_tab_change(:string) - Defaults tonil.- Global attributes are accepted.
Slots
tab- One tab panel. Accepts attributes:key(:string) (required)label(:string) (required)
empty_slot
Object-page orientation header: parent crumb, copyable ID, status, provenance, and return context.
Attributes
parent_label(:string) (required)parent_path(:string) (required)object_type(:string) (required)object_id(:string) (required)status(:any) - Defaults tonil.key_scalar(:string) - Defaults tonil.provenance(:string) - Defaults tonil.origin(:map) - Defaults tonil.class(:string) - Defaults tonil.id(:string) - Defaults tonil.- Global attributes are accepted.
Panel/card surface with optional eyebrow + title + actions header.
Attributes
variant(:atom) - Defaults to:flat. Must be one of:flat, or:raised.class(:string) - Defaults tonil.- Global attributes are accepted.
Slots
eyebrowtitleactionsinner_block(required)
Raw evidence details/pre block (DS-04). Renders a <details>/<summary> with a <pre> code block. Background and font tokens are applied via CSS class — not overridden in HEEx.
Attributes
label(:string) - Defaults to"Advanced raw evidence".
Slots
inner_block(required)
Loading skeleton placeholder (DS-05). Renders stacked skeleton lines with aria-label='Loading…' and role='status'. Pulse animation + prefers-reduced-motion suppression are handled by CSS.
Attributes
class(:string) - Defaults tonil.rows(:integer) - Defaults to1.- Global attributes are accepted.
Human label for a status string (title-cased, underscores → spaces).
Honest reserved-capability stub page. Future-tense only; no fake rows or charts.
Attributes
title(:string) (required)description(:string) (required)works_today(:list) - Defaults to[].tracking_url(:string) - Defaults tonil.class(:string) - Defaults tonil.
Sortable, density-aware, paginated data table (DS-01). Renders column headers from typed <:col> slots; emits sort events for keyed columns only when on_sort is supplied by the parent LiveView. Uses density modifier classes from density_class/1. Falls back to a default empty state when rows is empty (overridable via <:empty>). Pagination strip shown when total_pages > 1. Density controls render only when on_density_change is supplied by the parent LiveView.
Attributes
rows(:list) (required)sort_by(:any) - Defaults tonil.sort_dir(:atom) - Defaults to:asc. Must be one of:asc, or:desc.density(:atom) - Defaults to:default. Must be one of:compact,:default, or:comfortable.id(:string) (required)page(:integer) - Defaults to1.total_pages(:integer) - Defaults to1.on_sort(:string) - Defaults tonil.on_page_change(:string) - Defaults tonil.on_density_change(:string) - Defaults tonil.- Global attributes are accepted.
Slots
col- Table column. Accepts attributes:label(:string) (required)key(:atom)class(:string)
emptyactionfiltermobile_summary- Opt-in per-row mobile summary rendered in a sibling container hidden at >=768px. Exposes object label, status badge text, one key scalar/time, and a primary action. When absent, the table keeps honest overflow at all widths.
Transient toast notification (DS-05). Driven by a server @toasts assign. Auto-dismisses via phx-mounted={JS.hide(...)}. Manual dismiss X button is also provided. Does NOT use a JS hook (untestable in LiveViewTest). The phx-mounted auto-dismiss omits 'to:' so it self-targets the toast div (Pitfall 3); the dismiss button MUST target the toast by id (to: '#id') — a bare JS.hide there would hide the button, not the toast.
Attributes
id(:string) (required)tone(:atom) - Defaults to:neutral. Must be one of:pass,:fail,:warn,:info, or:neutral.message(:string) (required)duration_ms(:integer) - Defaults to4000.
Maps a domain status/kind string (or atom) to a semantic tone atom.
Tones: :pass | :info | :warn | :fail | :trace | :brand | :neutral. Unknown values
fall back to :neutral. This is the single source of truth for status coloring.