Feedback

callout

#existingsource

Inline contextual guidance inside document flow. Callout is for durable explanatory notes, warnings, and implementation gotchas that belong beside the content they qualify — it does not interrupt the reading position the way a viewport-pinned Banner does. It uses role=note so assistive tech treats it as supplementary rather than an interruption. For a page-level announcement use Banner; for transient feedback after an action use Toast.

Readiness
complete
Preview
live
Props
3
Examples
4
Code
implementation mapped

When to use

  • Notes or warnings inside generated docs and guides
  • Contextual implementation guidance that should not interrupt the workflow
  • Markdown blockquote-style asides that should render as first-class UI
  • A 'gotcha' beside a configuration field that needs explaining

When not to use

  • Page-level incidents or system announcements - use Banner
  • Transient feedback after an action - use Toast
  • Validation errors bound to a form field - use the field's error slot
  • A blocking decision that needs a response - use a Dialog

Accessibility

Role: note

  • No keyboard shortcuts

Screen reader: Rendered as an <aside role=note> so assistive tech announces it as a supplementary region in natural DOM order. The optional title is a real lead-in read before the body. The leading icon is decorative (aria-hidden).

Notes: Do not rely on tone colour alone. Include plain text such as Note, Warning, or Error (often as the title) when the semantic state matters.

Props

NameTypeDefaultDescription
toneenum: note | info | success | warning | errornote
titlestringOptional bold lead-in title.
iconstringOptional decorative leading icon key (e.g. info, warning).

Examples

Warning note

A documentation gotcha beside the prose it qualifies.

YAML
type: callout
props:
  tone: warning
  title: Heads up
  icon: warning
slots:
  default: Regenerating overwrites every file under generated/. Commit the YAML edit and the regenerated output together.
Info tip

An informational tip inside a guide.

YAML
type: callout
props:
  tone: info
  title: Tip
  icon: info
slots:
  default: Element behaviour flows from chemistry YAML through generated code — never hardcode element kinds in view code.
Plain note

A plain neutral aside with no title.

YAML
type: callout
props:
  tone: note
slots:
  default: This view is read-only for guest sessions.
Tones

All tones for visual comparison.

YAML
type: stack
props:
  gap: sm
  direction: column
children:
- type: callout
  props:
    tone: note
    title: Note
  slots:
    default: Neutral aside.
- type: callout
  props:
    tone: info
    title: Info
  slots:
    default: Informational tip.
- type: callout
  props:
    tone: success
    title: Done
  slots:
    default: Positive guidance.
- type: callout
  props:
    tone: warning
    title: Caution
  slots:
    default: Watch out for this.
- type: callout
  props:
    tone: error
    title: Error
  slots:
    default: Do not do this.
Edit YAML

card-skeleton

#existingsource

A ready-made card-shaped loading placeholder — a title bar plus three text lines (tapered widths) inside a card frame. It is intentionally zero-config: the fixed shape is the point, so a grid of loading cards reads as one coherent set instead of a jitter of differently-shaped placeholders. Render one per expected card while a card list loads.

Readiness
complete
Preview
live
Props
1
Examples
2
Code
implementation mapped

When to use

  • Loading state for a card list/grid — render N CardSkeletons matching the expected count.
  • First-load of a dashboard of Card tiles, before data arrives.
  • Anywhere you'd otherwise hand-fill a `<Card>` with LoadingSkeleton blocks.

When not to use

  • List rows (avatar + lines) — use `<ListSkeleton>`.
  • Table rows — use `<TableRowSkeleton>`.
  • An inline 'loading…' affordance — use `<LoadingInline>`.

Accessibility

Role: status

  • No keyboard shortcuts

Screen reader: `role=status` so AT announces the loading state without stealing focus. The skeleton bars are decorative; pair the loading region with a single polite "Loading…" announcement at the container level rather than one per skeleton.

Notes: Zero-config by design — the fixed title+3-line shape keeps a loading grid visually stable. Do not add per-instance shape props; that would reintroduce the jitter this primitive exists to remove.

Props

NameTypeDefaultDescription
shapeenum: fixedfixedDocumentation-only. CardSkeleton takes no runtime props — its shape (title + 3 tapered text lines in a card frame) is fixed by design for grid consistency. Listed so the catalog records the contract; not settable.

Examples

Card-list loading grid

A loading grid — three CardSkeletons standing in for a card list.

YAML
type: stack
props:
  direction: row
  gap: md
children:
- type: card
  props:
    style: flat
    padding: default
- type: card
  props:
    style: flat
    padding: default
- type: card
  props:
    style: flat
    padding: default
Single card skeleton

Single card placeholder — title bar + tapered lines.

YAML
type: card
props:
  style: flat
  padding: default
slots:
  default:
  - type: text
    slots:
      default: ▬▬▬▬▬▬ (title)
  - type: text
    slots:
      default: ▬▬▬▬▬▬▬▬▬▬
  - type: text
    slots:
      default: ▬▬▬▬▬▬▬▬
  - type: text
    slots:
      default: ▬▬▬▬
Edit YAML

empty-state

#existingsource

An empty-container affordance combining icon + title + body + optional CTA. EmptyState exists so "this list is empty" never reads as a dead-end — there is always a next action available (create something, invite someone, change the filter). For panel-bound empty states use PanelEmptyState (sibling, panel-density-aware).

Readiness
complete
Preview
live
Props
3
Examples
2
Code
implementation mapped

When to use

  • Empty list / table / search-result panel
  • First-run state for a feature ('No agents yet')
  • Filtered view with zero matches ('No agents match your filters')
  • Permission-gated views ('Sign in to see your projects')

When not to use

  • Loading state — use LoadingSpinner / LoadingSkeleton
  • Error state — use Banner / PanelErrorBanner
  • Inside a panel where panel-density-aware variant is needed — use PanelEmptyState

Accessibility

Role: status

  • No keyboard shortcuts

Screen reader: Reads heading + body via natural DOM order. CTA is announced as a button with its label. Keep heading concise — SR users hear it first and parse the rest in order.

Notes: The icon is decorative — set aria-hidden=true so SR users don't hear it announced. The textual content carries the meaning.

Props

NameTypeDefaultDescription
titlestringShort heading explaining the empty state.
bodystringLonger prose describing what to do next.
iconstringIcon name (decorative — aria-hidden).

Examples

Empty agents

Empty agent list with create CTA.

YAML
type: empty-state
props:
  title: No agents yet
  body: Create an agent to start automating tasks for this circle.
  icon: agents
slots:
  cta:
  - type: button
    props:
      variant: primary
      on_click: ${actions.create_agent}
    slots:
      default: Create agent
No matches

Filtered list with no matches and a clear-filter CTA.

YAML
type: empty-state
props:
  title: No matching results
  body: Try changing your filter or search term.
  icon: filter
slots:
  cta:
  - type: button
    props:
      variant: ghost
      on_click: ${actions.clear_filters}
    slots:
      default: Clear filters
Edit YAML

list-skeleton

#existingsource

A list-shaped loading placeholder — `count` rows, each an avatar plus a title line (40%) and a text line (70%). It mirrors the shape of a loaded list so the surface does not reflow when real data arrives. Set `count` to the expected row count where known; the default of 3 is a calm generic.

Readiness
complete
Preview
live
Props
1
Examples
2
Code
implementation mapped

When to use

  • Loading state for an avatar+text list (members, sessions, recent activity).
  • First-load of a panel list, sized to the expected row count to avoid reflow.
  • Anywhere you'd hand-fill an `<ItemList>` with LoadingSkeleton rows.

When not to use

  • Card grids — use `<CardSkeleton>`.
  • Table rows — use `<TableRowSkeleton>`.
  • A single inline 'loading…' — use `<LoadingInline>`.

Accessibility

Role: status

  • No keyboard shortcuts

Screen reader: `role=status`; skeleton rows are decorative. Announce loading once at the container level (polite), not once per skeleton row, so AT users aren't flooded.

Notes: Match `count` to the expected result size when you can — a skeleton that reflows to a very different row count on load defeats the no-reflow purpose.

Props

NameTypeDefaultDescription
countinteger3Number of skeleton rows. Set to the expected loaded row count to avoid reflow; default 3 is a generic calm placeholder.

Examples

List loading (count=4)

Members-list loading state — 4 avatar+text rows.

YAML
type: stack
props:
  direction: column
  gap: sm
children:
- type: text
  slots:
    default: ⬤  ▬▬▬▬   ▬▬▬▬▬▬▬
- type: text
  slots:
    default: ⬤  ▬▬▬▬   ▬▬▬▬▬▬▬
- type: text
  slots:
    default: ⬤  ▬▬▬▬   ▬▬▬▬▬▬▬
- type: text
  slots:
    default: ⬤  ▬▬▬▬   ▬▬▬▬▬▬▬
List loading (default)

Default 3-row generic list placeholder.

YAML
type: stack
props:
  direction: column
  gap: sm
children:
- type: text
  slots:
    default: ⬤  ▬▬▬▬   ▬▬▬▬▬▬▬
- type: text
  slots:
    default: ⬤  ▬▬▬▬   ▬▬▬▬▬▬▬
- type: text
  slots:
    default: ⬤  ▬▬▬▬   ▬▬▬▬▬▬▬
Edit YAML

loading-inline

#existingsource

A small spinner plus a short text label, laid out inline for in-flow loading — inside a button, beside a row action, mid-sentence. Use it for "something is happening right here, briefly" (saving, sending, reconnecting), not for first-load of a whole region (that is a skeleton's job). `text` defaults to "Loading…".

Readiness
complete
Preview
live
Props
1
Examples
2
Code
implementation mapped

When to use

  • Inline action feedback — "Saving…", "Sending…", "Reconnecting…" beside or inside a control.
  • A brief pending state where the surrounding layout should not shift.
  • Replacing a bare 'Loading...' string with a consistent spinner+text affordance.

When not to use

  • First-load of a list/card/table region — use the matching skeleton (preserves layout shape).
  • A full-panel loading state — use `<PanelLoadingState>`.
  • A long operation with progress — use a progress indicator, not an indefinite inline spinner.

Accessibility

Role: status

  • No keyboard shortcuts

Screen reader: `role=status` so the pending state is announced politely. Keep `text` a short verb phrase ("Saving…") — it is the announced content; the spinner is decorative. Clear/replace it promptly when the operation resolves so AT users get the outcome.

Notes: Indefinite by nature — for operations long enough to need ETA or percentage, use a determinate progress indicator instead.

Props

NameTypeDefaultDescription
textstringLoading...Short verb-phrase label beside the spinner ("Saving…", "Sending…"). It is the SR-announced content; keep it terse.

Examples

Inline saving

In-button saving state — spinner + 'Saving…'.

YAML
type: stack
props:
  direction: row
  gap: sm
children:
- type: spinner
- type: text
  slots:
    default: Saving…
Default loading

Default label beside a row action.

YAML
type: stack
props:
  direction: row
  gap: sm
children:
- type: spinner
- type: text
  slots:
    default: Loading…
Edit YAML

loading-skeleton

#existingsource

A content-shaped placeholder that shimmers while the real content is loading. Use Skeleton (not Spinner) when the layout would otherwise jump as content lands, or when the user benefits from knowing the shape of what's coming. For specific common shapes, use the dedicated variants: CardSkeleton, ListSkeleton, TableRowSkeleton.

Readiness
complete
Preview
live
Props
3
Examples
2
Code
implementation mapped

When to use

  • Above-the-fold content where layout shift is jarring
  • Repeating list / card layouts (use specific variants)
  • Long-running fetches where shape signals to the user what to expect
  • Pre-rendering placeholders for SSR / hydration windows

When not to use

  • Brief loads (<200ms) — show nothing
  • Action affordances (button click, form submit) — use Spinner
  • Errors — use Banner / EmptyState
  • Content with unknown shape — Spinner is better than a misleading skeleton

Accessibility

Role: status

  • No keyboard shortcuts

Screen reader: Skeleton elements should declare aria-busy=true on their containing region so SR users know content is loading. The shimmer animation itself is not announced.

Notes: Don't use Skeleton for content that may never load (e.g. permission- gated). Use EmptyState instead.

Props

NameTypeDefaultDescription
widthstringCSS width (e.g. '100%', '120px').
heightstringCSS height (e.g. '1rem', '24px').
roundedbooleanfalseWhether to round corners (for avatar / pill placeholders).

Examples

Article placeholder

Title + body skeleton above-the-fold.

YAML
type: stack
props:
  gap: sm
  direction: column
children:
- type: loading-skeleton
  props:
    width: 60%
    height: 1.5rem
- type: loading-skeleton
  props:
    width: 100%
    height: 1rem
- type: loading-skeleton
  props:
    width: 100%
    height: 1rem
- type: loading-skeleton
  props:
    width: 80%
    height: 1rem
User row

Avatar + name placeholder.

YAML
type: stack
props:
  gap: sm
  direction: row
  align: center
children:
- type: loading-skeleton
  props:
    width: 32px
    height: 32px
    rounded: true
- type: loading-skeleton
  props:
    width: 120px
    height: 1rem
Edit YAML

notification-bus

#existingsource

The non-visual coordination layer for transient notifications: a single bus that `<Toast>`, ambient-notification (`unified_bar/ambient_renderer`), and optimistic-error-toast (`optimistic/components`) should all publish into and render from, instead of three parallel systems. It owns the notification model — kind (info/success/warning/error), tier, optional celebration/confirm payloads, and actions — while visual treatment lives in the renderers. It lives in `portal/src/canvas/notification_bus.rs` and is catalogued so future visual work routes through it.

Readiness
complete
Preview
live
Props
3
Examples
2
Code
implementation mapped

When to use

  • Emitting any transient notification — publish to the bus, let a renderer (Toast / ambient) display it.
  • Building a new notification surface — subscribe to the bus, do NOT invent a parallel queue.
  • Coordinating dedupe / tiering across notification sources.

When not to use

  • Rendering — this is coordination only; visual treatment is `<Toast>` / ambient renderers.
  • Persistent state or audit logs — the bus is for transient notifications, not durable records.
  • A one-off in-component message with no cross-system concern — a local signal is simpler.

Accessibility

Role: presentation

  • No keyboard shortcuts

Screen reader: Not rendered — no direct AT surface. Its accessibility value is indirect: by funnelling all transient notifications through one bus, the renderers can own exactly one well-behaved `aria-live` region instead of three competing ones talking over each other.

Notes: Coordination layer — visual + aria-live treatment lives in `<Toast>` / `<ToastStack>` / ambient renderers. Catalog records the shared contract for `portal::canvas::notification_bus::NotificationBus`.

Props

NameTypeDefaultDescription
kindenum: info | success | warning | errorNotification semantic kind. Renderers map this to tone/icon/ aria-live politeness. Required on publish.
tierenum: ambient | toast | celebration | confirmWhich renderer/treatment the notification routes to — ambient bar, transient toast, celebration, or a confirm prompt.
actionsarrayOptional action descriptors carried with the notification so the renderer can offer buttons (Retry, Undo, View).

Examples

Publish: success → toast

Publishing a success notification routed to the toast tier (data shape, not a render).

YAML
type: stack
props:
  direction: column
  gap: sm
children:
- type: text
  slots:
    default: 'kind: success'
- type: text
  slots:
    default: 'tier: toast'
- type: text
  slots:
    default: 'message: "Element published"'
Publish: error + action

An error with a Retry action, routed to the toast tier.

YAML
type: stack
props:
  direction: column
  gap: sm
children:
- type: text
  slots:
    default: 'kind: error · tier: toast'
- type: text
  slots:
    default: 'actions: [ { label: Retry, action: ${actions.retry} } ]'
Edit YAML

progress-bar

#existingsource

A linear progress indicator for a single quantifiable task: an upload, an import, a quota fill, a multi-file operation. In determinate mode the fill width tracks value/max and the rounded percentage is announced via aria-valuenow. In indeterminate mode the bar shows a travelling segment and drops aria-valuenow so assistive tech says "busy" instead of a misleading number. For multi-step flow position use ProgressSteps; for a tiny liveness marker use StatusDot.

Readiness
complete
Preview
live
Props
6
Examples
4
Code
implementation mapped

When to use

  • Upload / download / import progress with a known total
  • A quota or usage meter (value out of max)
  • A long operation whose completion is unknown (indeterminate mode)
  • Batch operation progress ('312 of 500 processed')

When not to use

  • Discrete steps in a wizard — use ProgressSteps
  • A tiny on/off liveness marker — use StatusDot
  • A radial / dial visual — use Gauge or DonutChart
  • An indeterminate spinner with no bar semantics — use LoadingSpinner

Accessibility

Role: progressbar

  • No keyboard shortcuts

Screen reader: The track is role=progressbar with aria-valuemin=0 and aria-valuemax=max. In determinate mode aria-valuenow carries the clamped current value; in indeterminate mode aria-valuenow is omitted so assistive tech announces a busy state rather than a false percentage. The visible label also backs the accessible name.

Notes: Always pass a label so the bar has an accessible name. The numeric percentage is rendered as text (show_value) so it is not colour- or width-only.

Props

NameTypeDefaultDescription
valuenumber0Current value, clamped to 0..=max. Ignored when indeterminate.
maxnumber100Upper bound. Non-positive values fall back to 100.
toneenum: default | success | warning | dangerdefault
indeterminatebooleanfalseUnknown-completion mode (animated sweep, reduced-motion safe).
show_valuebooleanfalseRender the rounded percentage beside the track.
labelstringAccessible / visible label for the bar.

Examples

Determinate

An upload in progress with the percentage shown.

YAML
type: progress-bar
props:
  value: 42
  max: 100
  label: Uploading
  show_value: true
Indeterminate

A long operation whose completion is unknown.

YAML
type: progress-bar
props:
  indeterminate: true
  label: Working
Quota

A quota near its limit, in the danger tone.

YAML
type: progress-bar
props:
  value: 94
  max: 100
  tone: danger
  label: Storage used
  show_value: true
Tones

All tones for visual comparison.

YAML
type: stack
props:
  gap: sm
  direction: column
children:
- type: progress-bar
  props:
    value: 60
    tone: default
    label: Default
    show_value: true
- type: progress-bar
  props:
    value: 100
    tone: success
    label: Complete
    show_value: true
- type: progress-bar
  props:
    value: 75
    tone: warning
    label: Warning
    show_value: true
- type: progress-bar
  props:
    value: 94
    tone: danger
    label: Danger
    show_value: true
Edit YAML

progress-steps

#existingsource

A horizontal indicator of position within a discrete, ordered flow: an onboarding wizard, a multi-page form, a setup checklist. Steps before the current one render complete (check marker); the current step is emphasised; later steps are upcoming. State is conveyed by marker glyph plus colour plus visually-hidden text, never colour alone. For a continuous quantity use ProgressBar; for a single liveness marker use StatusDot.

Readiness
complete
Preview
live
Props
2
Examples
3
Code
implementation mapped

When to use

  • An onboarding or setup wizard ('Account → Profile → Confirm')
  • A multi-page form where the user should see how far they are
  • A checkout / submission flow with discrete stages
  • Any bounded sequence where step position matters

When not to use

  • Continuous / percentage progress — use ProgressBar
  • A single liveness marker — use StatusDot
  • Free navigation between unrelated sections — use Tabs
  • An unbounded activity feed — use a Timeline

Accessibility

Role: navigation

  • No keyboard shortcuts

Screen reader: Wrapped in <nav aria-label="Progress"> with an ordered list. The in-progress step carries aria-current="step". Each step appends visually-hidden text ('(complete)' / '(current)' / '(upcoming)') and a 'Step N of M' summary so the position is conveyed without relying on the marker colour. Markers are aria-hidden decoration.

Notes: State is conveyed by glyph (check vs number), colour, and screen-reader text together — never colour alone.

Props

NameTypeDefaultDescription
stepsarrayOrdered step labels.
currentinteger0Zero-based index of the in-progress step. Earlier steps are complete; later steps are upcoming.

Examples

Onboarding

A three-step onboarding wizard on the middle step.

YAML
type: progress-steps
props:
  steps:
  - Account
  - Profile
  - Confirm
  current: 1
Checkout start

A checkout flow with the first step active.

YAML
type: progress-steps
props:
  steps:
  - Cart
  - Shipping
  - Payment
  - Review
  current: 0
Final step

A flow on its final step (all prior steps complete).

YAML
type: progress-steps
props:
  steps:
  - Account
  - Profile
  - Confirm
  current: 2
Edit YAML

spinner

#existingsource

Visible spinner plus a polite live region for the status text. Always pair with a label slot — even decorative spinners benefit from a label hidden to sighted users but read by SR. Use `inline` when the spinner sits next to text in a row, `block` when it owns its own line.

Readiness
complete
Preview
live
Props
2
Examples
3
Code
implementation mapped

When to use

  • Known operation in progress (>250ms wait)
  • User-initiated action awaiting server response
  • Inline next to a button label (`layout: inline`)
  • Block-level loading screen for a section (`layout: block`)
  • Background refresh of visible data (with subtle inline placement)

When not to use

  • Skeleton-shaped placeholders for content layout — use LoadingSkeleton
  • Brief flashes (<100ms) — no indicator at all
  • Page navigation — use a route progress bar
  • Indeterminate operations >10s — switch to a progress bar with cancel

Accessibility

Role: status

  • No keyboard shortcuts

Screen reader: `role=status` + `aria-live=polite` ensures SR announces the label text when it changes (e.g. "Verifying molecular signature..." → "Almost there..."). Decorative spinners SHOULD still have a label hidden via visually-hidden CSS so SR users get an announcement.

Notes: The label is the load-bearing accessibility input. A spinner without a label is invisible to SR users entirely.

Props

NameTypeDefaultDescription
sizeenum: small | medium | largemedium
layoutenum: inline | blockblock`inline` keeps the spinner on the baseline of its row (label sits next to it). `block` centres the spinner above its label.

Examples

Block spinner with status

Login overlay loading state with announced status text.

YAML
type: spinner
props:
  size: medium
  layout: block
slots:
  label:
  - type: text
    props:
      kind: muted
    slots:
      default: ${state.loading_phase}
Inline spinner

Inline spinner in a button's loading state (handled by button itself, here for reference).

YAML
type: spinner
props:
  size: small
  layout: inline
Sizes gallery

All sizes side-by-side.

YAML
type: stack
props:
  gap: lg
  direction: row
  align: center
children:
- type: spinner
  props:
    size: small
    layout: block
  slots:
    label:
    - type: text
      slots:
        default: small
- type: spinner
  props:
    size: medium
    layout: block
  slots:
    label:
    - type: text
      slots:
        default: medium
- type: spinner
  props:
    size: large
    layout: block
  slots:
    label:
    - type: text
      slots:
        default: large
Edit YAML

status-dot

#existingsource

A small coloured dot communicating connection / liveness state at a glance: online, offline, error, idle, busy, or unknown. Colour is never the sole signal — the component always supplies an accessible name from the tone, and an optional visible label is encouraged. The pulse option draws attention to an active state and is suppressed under reduced motion. For a labelled status use Badge; for numeric progress use ProgressBar.

Readiness
complete
Preview
live
Props
3
Examples
4
Code
implementation mapped

When to use

  • Agent / service liveness next to a name ('● Online')
  • Connection state in a header or rail item
  • Row-level health indicator in a dense list
  • A pulsing 'live' marker on an actively streaming resource

When not to use

  • A textual status label or count — use Badge
  • Linear completion progress — use ProgressBar
  • Multi-step flow position — use ProgressSteps
  • An interactive toggle — use Switch

Accessibility

Role: status

  • No keyboard shortcuts

Screen reader: The wrapper is role=status with an aria-label drawn from the tone (e.g. 'Online') when no visible label is given, so the state is announced even though the dot itself is aria-hidden decoration. A supplied visible label backs the accessible name instead.

Notes: Pair with a text label — colour alone fails for colourblind users. The default per-tone label exists precisely so a dot-only usage is still announced.

Props

NameTypeDefaultDescription
toneenum: online | offline | error | idle | busy | neutralonline
pulsebooleanfalsePulse animation (suppressed under prefers-reduced-motion).
labelstringOptional visible text label rendered beside the dot.

Examples

Online

Agent liveness with a visible label.

YAML
type: status-dot
props:
  tone: online
  label: Connected
Live

A pulsing marker on an actively streaming resource.

YAML
type: status-dot
props:
  tone: busy
  pulse: true
  label: Streaming
Error

Error state in a dense row.

YAML
type: status-dot
props:
  tone: error
  label: Disconnected
Tones

All tones for visual comparison.

YAML
type: stack
props:
  gap: md
  direction: row
  align: center
children:
- type: status-dot
  props:
    tone: online
    label: Online
- type: status-dot
  props:
    tone: offline
    label: Offline
- type: status-dot
  props:
    tone: error
    label: Error
- type: status-dot
  props:
    tone: idle
    label: Idle
- type: status-dot
  props:
    tone: busy
    label: Busy
- type: status-dot
  props:
    tone: neutral
    label: Unknown
Edit YAML

table-row-skeleton

#existingsource

One `<tr>` of `columns` placeholder-text cells. Render several inside a `<tbody>` while table data loads so column widths and row height stay stable — the table does not jump when real rows replace the skeletons. Match `columns` to the real table's column count.

Readiness
complete
Preview
live
Props
1
Examples
2
Code
implementation mapped

When to use

  • Loading state for a data table — N TableRowSkeletons in the tbody.
  • Keeping column widths stable across the load transition.
  • Anywhere you'd hand-fill `<tr>`s with LoadingSkeleton cells.

When not to use

  • Non-table lists — use `<ListSkeleton>`.
  • Card grids — use `<CardSkeleton>`.
  • A single inline 'loading…' — use `<LoadingInline>`.

Accessibility

Role: status

  • No keyboard shortcuts

Screen reader: `role=status`; cells are decorative. Announce loading once at the table/container level (polite) rather than per skeleton row. Ensure the real table has proper header associations once loaded.

Notes: Set `columns` to the real column count — a skeleton with the wrong column count reflows the table on load, defeating the purpose.

Props

NameTypeDefaultDescription
columnsinteger4Number of placeholder cells in the row. Match the real table's column count to keep widths stable across load.

Examples

Table loading (columns=5)

5-column table loading — three skeleton rows.

YAML
type: stack
props:
  direction: column
  gap: sm
children:
- type: text
  slots:
    default: ▬▬  ▬▬  ▬▬  ▬▬  ▬▬
- type: text
  slots:
    default: ▬▬  ▬▬  ▬▬  ▬▬  ▬▬
- type: text
  slots:
    default: ▬▬  ▬▬  ▬▬  ▬▬  ▬▬
Table row (default 4 cols)

Default 4-column skeleton row.

YAML
type: text
slots:
  default: ▬▬  ▬▬  ▬▬  ▬▬
Edit YAML

toast

#existingsource

A floating, auto-dismissing transient notification. Call sites push via `ToastContext` — `push(msg, level)`, `push_icon_only(level)`, `push_with_action(msg, level, …)`, `dismiss(id)` — and the toast renders + auto-expires in the `<ToastStack>` mounted at app root. `level` is the shared tone enum (info / success / warning / error).

Readiness
complete
Preview
live
Props
4
Examples
2
Code
implementation mapped

When to use

  • Confirming a completed action that doesn't need acknowledgement — "Changes saved".
  • Non-blocking warnings/errors the user should notice but not be interrupted by.
  • Icon-only success pings for high-frequency low-importance actions (`push_icon_only`).

When not to use

  • Errors that must be acted on before continuing — use a `<PanelErrorBanner>` or modal.
  • Persistent status — use `<WorkspaceStatus>` / `<PanelFooter>` status slot.
  • A new parallel notification path — route through `<NotificationBus>`; don't add a 4th system.

Accessibility

Role: status

  • Escape — Dismiss the focused/last toast.

Screen reader: `aria-live=polite` for info/success/warning so the toast is announced without interrupting; `aria-live=assertive` for errors. Auto-dismiss timing must be long enough for SR users to hear it — pair critical content with `push_with_action` so it isn't lost to a timeout.

Notes: `portal::components::toast::Toast` provides the presentational row; `ToastContext` remains the app-facing push API.

Props

NameTypeDefaultDescription
messagestringToast body text. Required (except `push_icon_only`).
levelenum: info | success | warning | errorinfoShared tone enum (`ToastLevel`). Drives colour + aria-live politeness.
icon_onlybooleanfalseRender just the level icon (high-frequency low-importance pings).
actionstringOptional action label (`push_with_action`) — renders a button so the toast survives long enough to be acted on.

Examples

Success toast

Success confirmation — the canonical 'Changes saved' ping.

YAML
type: toast
props:
  message: Changes saved
  level: success
Error toast + action

Error toast with an action (push_with_action) so it's not lost to timeout.

YAML
type: toast
props:
  message: Save failed
  level: error
  action: Retry
Edit YAML

toast-stack

#existingsource

The singleton container that makes `<Toast>` work: mounted once at the app root, it subscribes to the toast bus, renders queued toasts with stacking rules and exit transitions, and owns the `aria-live` region toasts announce into. Call sites never render it directly — they `push` via `ToastContext` and ToastStack does the rest.

Readiness
complete
Preview
live
Props
2
Examples
2
Code
implementation mapped

When to use

  • Exactly once, at the application root, so every surface's toasts have a renderer.
  • As the single mount point — never per-panel; multiple stacks fight over the same queue.
  • When wiring a new app shell that needs toast support.

When not to use

  • Per-surface or per-panel — the stack is a singleton by contract.
  • To push a toast — use `ToastContext::push`, not a direct render.
  • As a general overlay container — it is toast-queue-specific.

Accessibility

Role: region

  • No keyboard shortcuts

Screen reader: Owns the `aria-live` region; individual toasts set polite/assertive per level. As a singleton it ensures there is exactly one live region for transient notifications — multiple stacks would create competing live regions that talk over each other.

Notes: `portal::components::toast::ToastStack` is the singleton mount point for ambient toast rendering.

Props

NameTypeDefaultDescription
placementenum: top-right | top-center | bottom-right | bottom-centerbottom-rightWhere the stack anchors on screen. One value per app — toasts from all surfaces share this placement.
max_visibleinteger4How many toasts render at once; the rest queue and surface as earlier ones dismiss. Prevents an unbounded wall of toasts.

Examples

Toast stack (bottom-right)

Bottom-right stack with two queued toasts (the common shape).

YAML
type: stack
props:
  direction: column
  gap: sm
children:
- type: text
  slots:
    default: ✓ Changes saved
- type: text
  slots:
    default: ⚠ Connection unstable
Stack at max_visible

Overflow — max_visible reached, remaining toasts queued.

YAML
type: stack
props:
  direction: column
  gap: sm
children:
- type: text
  slots:
    default: ✓ Saved
- type: text
  slots:
    default: ✓ Synced
- type: text
  slots:
    default: ✓ Deployed
- type: text
  slots:
    default: ✓ Published
- type: text
  slots:
    default: +2 more queued
Edit YAML