Skip to content
Components

Banner, toast & empty state

Three messages about a view rather than about a subject in it — a banner that stays, a toast that confirms and leaves, and an empty state that says what would fill the space.

Verdict banner

No approval needed

Keep the shed under 10 m² and behind the building line and you may start today.

Decided by nobody — it's exempt Typically 0 days
Building + council

A standard council check against the written rulebook. Tick every box and it must be approved; neighbours can't formally object.

Decided by Riverton City Council Typically 6–12 weeks
Code
<GdVerdictBanner
  title="Building + council"
  :required="['building', 'council']"
  decided-by="Riverton City Council"
  time="6–12 weeks"
>
  A standard council check against the written rulebook. Tick every box
  and it must be approved; neighbours can't formally object.
</GdVerdictBanner>

Page banners

Scheme amendment on public notice. Riverton City has proposed changes to this zone. Submissions close 14 September.

You have 1 free check left. Pro gives unlimited checks, saved projects and branded reports.

Code
<GdBanner dot="caution" dismissible @dismiss="hide">
  <strong>Scheme amendment on public notice.</strong> Submissions close 14 September.
  <template #action><GdButton size="sm">Read it</GdButton></template>
</GdBanner>

<GdBanner tone="accent" dismissible @dismiss="hide">
  <strong>You have 1 free check left.</strong> Pro gives unlimited checks.
  <template #action><GdButton size="sm" variant="primary">See Pro</GdButton></template>
</GdBanner>

Toasts

Parcel saved to Jarrah St project
Bottom-centre, five seconds, then gone.
Code
<GdToast action="View" @action="openProject">
  Parcel saved to Jarrah St project
</GdToast>

The system

The component above is only the drawn box. The SYSTEM is useGdNotify() plus GdToastHost: the app mounts the host once at the end of its shell, and from then on anything — a composable, a poll, a catch block — calls notify() and the notice appears bottom-centre, above everything, owned by no panel. That last part is the point: the notifications that matter most are about work that outlives the surface that started it, and a toast a panel mounts dies with the panel.

Live — these buttons drive the real queueTone decides the behaviour, and the split is GdToast's own doctrine enforced by the system. success and info are transient: five seconds, gone, because they confirm what you already asked for. caution and danger are STICKY: they stay until dismissed, with the close affordance the transient forms never show — an error that auto-dismisses is an error you have decided the user may miss. Hovering the stack holds every timer.
Code
// once, at the end of the app shell
<GdToastHost />

// anywhere at all
const { notify } = useGdNotify()
notify("Parcel saved to Jarrah St project", { action: "View", onAction: openProject })
notify("Zone clip failed", { tone: "danger", action: "Retry", onAction: retry })

Empty states

No saved parcels yet

Search for an address and save it to a project. Saved parcels keep their scenarios and re-check themselves when the scheme changes.

Code
<GdEmptyState
  title="No saved parcels yet"
  icon="M9 20H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-5"
>
  Search for an address and save it to a project.
  <template #action>
    <GdButton variant="primary" size="sm">Search an address</GdButton>
  </template>
</GdEmptyState>

API

GdVerdictBanner

Props

PropTypeDefault
titlerequiredstring—

The plain-English verdict — "Building + council". `stackLabel()` produces exactly this string from a set of authorities.

requiredreadonly ApprovalAuthority[]—

The authorities, rendered as an icons-only stack beside the title. Omitted or empty, the stack reads "No approval needed".

decidedBystring—

Who decides — "a private certifier", "Riverton City Council". Renders in the footer strip.

timestring—

The typical duration — "2–4 weeks". The second of the two facts a person plans around.

Slots

default

One paragraph saying what the verdict means in practice. The footer strip only appears when `decidedBy` or `time` is set.

GdBanner

Props

PropTypeDefault
tone"ink" | "accent""ink"

The ground. `ink` is the attention-getting near-black surface; `accent` is the softer product message. Never a status hue.

dot"success" | "info" | "caution" | "danger"—

A 7px status dot on the left — the condition's severity, not the banner's.

dismissibleboolean—

Adds the close button. It emits `dismiss` and nothing else — the banner does not hide itself.

Slots

default

The message. A `<strong>` lead sentence then the detail; the banner styles that emphasis for its own ground.

action

One button on the right.

Events

dismiss

The close button was pressed. Hiding it, and remembering that, is the caller's.

GdToast

Props

PropTypeDefault
tone"success" | "info" | "caution" | "danger""success"

Picks the glyph and its on-dark colour. `danger` renders role="alert"; the other three render role="status".

actionstring—

The label of the single undo-or-view affordance. One only.

dismissibleboolean—

Show a close affordance and emit `dismiss`. The host sets this on sticky notices; a close button on a five-second toast is an invitation to chase it.

Slots

default

The confirmation, in one line.

Events

action

The action link was pressed.

GdToastHost · useGdNotify

The host takes no props — the queue is the API. notify(message, options?) returns an id; options is { tone, action, onAction, sticky }, with sticky defaulted from the tone. dismiss(id) removes a notice early. Client-side calls only: a notification raised during server render has no one to see it, and callbacks cannot cross the SSR payload.

GdEmptyState

Props

PropTypeDefault
titlerequiredstring—

What is not here. "No saved parcels yet", not "No data".

iconstring—

A 24×24 SVG PATH, not a registry name — drawn in a 64px tile, the one place a tile gets real size and a shadow. An icon name here silently renders an empty tile; use the icon slot instead.

Slots

default

The body: what would put something here.

icon

The glyph, for an app with its own icon set. Use this instead of the icon prop — anything but a path needs a component.

action

The button that does it. Leave it out for a search that found nothing.

Usage Guidelines

  • Use a banner for a condition of the whole VIEW that stays until it changes — a scheme amendment, a quota, an outage.
  • Mount GdToastHost once, in the shell. A host per panel is a toast that dies with the panel — which is backwards, because the notices that matter most outlive the surface that raised them.
  • Use a toast only to confirm something the user just asked for. Anything they must read to avoid harm is an alert or a dialog.
  • Use an alert when the message is about the DATA rather than the view, and belongs beside the thing it is about.
  • Use tone="ink" when the banner must be noticed, and accent for a product message that can wait. Put severity on dot, never on the ground.
  • Handle @dismiss yourself and persist the answer where the preference belongs — the banner will not hide itself.
  • Give an empty state a body and an action, except after a search that found nothing: the user already knows what to do.
  • Pass the empty state's glyph as a path, or use #icon — icon is a d attribute, so a name goes in as <path d="folder"> and draws nothing. It is valid SVG, so nothing errors; the tile just comes out blank.
  • Reach for the verdict banner only for the planning answer itself. It is one component with one job, not a general result panel.

Behavior & Anatomy

The verdict banner

It never animates its wording: an icon joins or leaves the stack the instant a threshold is crossed, so a changing answer reads as a change of state rather than as a loading state.

Page banners

Dismissal is the caller's to persist. A banner that reappears on every route change has not been dismissed, it has been ignored — and the component cannot know where that preference belongs.

Toasts

The surface is dark in BOTH themes — a toast is not a card, and making it dark is how it reads as transient chrome rather than as content that has appeared in the page.

Empty states

An empty state with only a title is a dead end. The body says what would put something here, and the action does it. The one case where an action is wrong is a search that found nothing: the user already knows what to do, and a button that says "Search again" is noise.

Navigate

Esc