VII Signals

Star on GitHubFeedback
VII SignalsNative HTML and a hookot-toast

Toast

One line rises from the bottom-left; a fermata held over its last word empties while it stays.

Report an issue with Toast

Example

Toasts as
Fermata

Changes saved.

Fermata, with an action

Halden archived.

Footnote, two notes

Changes saved.

Halden archived.

Dateline

Changes saved.

Install

Run it in a project that has the shadcn CLI set up. It brings the 0nlyType base and button along.

npx shadcn@latest add https://thedirectors.agency/ui/r/toast.json

Usage

This example also uses Radio group.

npx shadcn@latest add https://thedirectors.agency/ui/r/radio-group.json
toast-example.tsx
"use client"import * as React from "react"import { Button } from "@/components/ui/button"import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"import { Toast, Toaster, dismiss, toast, type ToastVariant } from "@/components/ui/toast"const still = () => () => {}export default function Example() {const [variant, setVariant] = React.useState<ToastVariant>("fermata")// The site already mounts a Toaster; this one mounts a commit later, so it's the latest and speaks for the page.const mounted = React.useSyncExternalStore(still, () => true, () => false)return (<div className="grid justify-items-start gap-(--ot-space-7)"><RadioGroup legend="Toasts as" value={variant} onValueChange={(v) => setVariant(v as ToastVariant)}><RadioGroupItem value="fermata">Fermata</RadioGroupItem><RadioGroupItem value="footnote">Footnote</RadioGroupItem><RadioGroupItem value="dateline">Dateline</RadioGroupItem></RadioGroup><div className="flex flex-wrap items-baseline gap-10"><Button onClick={() => toast("Changes saved.")}>Save changes</Button><ButtononClick={() =>toast("Halden archived.", {action: { label: "Undo", onClick: () => toast("Halden restored.") },})}>Archive project</Button><Button variant="quiet" onClick={() => dismiss()}>Clear all</Button></div>{mounted ? <Toaster variant={variant} /> : null}</div>)}// Pinned: a toaster set in place, its clock held as if pointed at.function Still({ variant, children }: { variant: ToastVariant; children: React.ReactNode }) {return (<div className="ot-toaster" data-variant={variant === "fermata" ? undefined : variant} style={{ position: "static", zIndex: "auto", maxInlineSize: "100%" }}>{children}</div>)}

Contract

  • Underneath: hook: a tiny store and a <Toaster />.
  • Anatomy: .ot-toaster (aria-live="polite", fixed bottom left), holding .ot-toast elements. Each is a sentence, svg.ot-toast-timer (a fermata: arc pathLength="1" plus an accent dot) hung up against its full stop, the way the sign is written over the note it holds, and an optional bracket action.
  • The fermata's arc empties over --life. Pointing at the toast or focusing it holds the pause, which is literally what a fermata means. At most three toasts show at once. The toast rises, then its sentence is written in from the left. It leaves with data-leaving: it sinks and fades, then its height closes so the toasts above settle down into the space.
  • Yours: the item's name inside the sentence, e.g. "Archived Spring notes."
  • variant sits on the Toaster (data-variant); each keeps the fermata as its clock, unseen, and pointing still holds the stay. footnote: the book's note at the foot of the page. A short ink rule draws in over the first note, and each note carries its number (.ot-toast-n) hung in the margin so the sentences keep one edge under the rule. Numbers count up while notes stand and start again at 1 on an empty page; only the newest takes the accent, the rest are pencil. One frame holds the whole foot, not one per note. dateline: the frame row of a poster, each toast in its frame holding a sentence, a hairline and the time it happened (time.ot-toast-time, the reader's own clock). The hairline (.ot-toast-rule) runs out toward the time while the toast stays. Right to left, the rule runs out the other way and the sentence writes in from the right. When a page mounts a second <Toaster />, the one mounted last shows the toasts and the others fall silent, so nothing is read out twice.

Props

Props of Toast
PropTypeDefaultWhat it does
toast(message, opts?)(message: ReactNode, opts?: { action?: { label: string; onClick: () => void }; duration?: number }) => stringSay one thing. Returns the toast's id. At most three stay on screen; an action keeps its name (Undo).
opts.action{ label: string; onClick: () => void }One bracket button. Choosing it runs onClick and sends the toast away.
opts.durationnumber5000How long it stays, in milliseconds. Pointing at the toast, or focusing inside it, holds the pause.
dismiss(id?)(id?: string) => voidSend one toast away, or every toast when no id is given.
<Toaster />componentRender once, near the root. It is a polite live region, so each toast is announced when it arrives. If more than one is mounted, the latest one shows the toasts and the rest stay quiet.
<Toaster variant>"fermata" | "footnote" | "dateline""fermata"fermata: each toast is framed, and a fermata hangs over its last word; the arc empties while it stays. footnote: the notes sit at the foot of the page under a short rule, numbered as they come, the number hung in the margin, the newest in the accent; an empty page starts again at 1. dateline: the sentence and the time it happened on one hairline, which runs out toward the time. In all three, pointing at a toast or focusing inside it holds its stay.
<Toast />componentOne toast, as the Toaster draws it. Render it yourself only to show one still, inside a .ot-toaster, as the docs do.

Craft

Where its move comes from

Where its move comes from
ComponentReferenceMove
ToastThe fermata signThe arc over the last word empties; pointing holds it
Footnote toastA book's footnotesNumbered notes under a short rule
Dateline toastSHAPES / GRADIENTS frame rowThe sentence and its time on a hairline that runs out

How it moves

How it moves
ComponentArticulationWhat moves
ToastWrittenIt rises, the sentence writes in, and the stack closes over the gap when it leaves
Footnote toastSpiccatoThe rule draws in over the first note; each note rises and its number lands
Dateline toastHeldThe hairline runs out toward the time, and holds while pointed at