Westy Design Systemv0.2.7
OverviewComponentsExamplesStorybook

Foundations

  • Typography
  • Colors
  • Spacing
  • Radius
  • Shadows
  • Icons

Components

  • Accordion
  • Alert
  • Alert Dialog
  • Aspect Ratio
  • Avatar
  • Badge
  • Breadcrumb
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Data Table
  • Date Picker
  • Dialog
  • Direction
  • Drawer
  • Dropdown Menu
  • Empty
  • Field
  • Hover Card
  • Input
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Label
  • Menubar
  • Native Select
  • Navigation Menu
  • Pagination
  • Popover
  • Progress
  • Questionnaire
  • Radio Group
  • Resizable
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Skeleton
  • Slider
  • Sonner
  • Spinner
  • Switch
  • Table
  • Tabs
  • Textarea
  • Toggle
  • Toggle Group
  • Tooltip

Chat

  • Attachment
  • Bubble
  • Marker
  • Message
  • Message Scroller

Components

Sonner

An opinionated toast component for React.

Playground

—toast[type]
—position
—description
—action
—richColors
—closeButton
Usage
Two imports, from two places. Toaster — the Westy wrapper that wires the next-themes theme, Phosphor icons and popover tokens — is mounted once near the end of the root layout; it already is in this showcase. Toasts are then fired imperatively from anywhere with the toast() function imported from the sonner package itself — there is no per-call component to render.
// app/layout.tsx — mount once
import { Toaster } from "@workspace/ui/components/sonner"

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Toaster />
      </body>
    </html>
  )
}

// any client component
import { toast } from "sonner"

<Button variant="outline" onClick={() => toast("Event has been created.")}>
  Show toast
</Button>
Types
toast.success, toast.info, toast.warning and toast.error prepend the matching Phosphor icon configured in the wrapper (CheckCircle, Info, Warning, XCircle), so state is conveyed by an icon as well as by the variant's color.
toast.success("Changes saved")
toast.info("A new version is available")
toast.warning("Storage is almost full")
toast.error("Something went wrong")
Description
Pass description for a second, muted line under the title. Keep the title short and put the detail — dates, file names, counts — in the description.
toast("Event has been created", {
  description: "Monday, January 3rd at 6:00pm",
})
Action
action renders a small button at the end of the toast; clicking it runs onClick and dismisses the toast. An Undo action is the classic use — it turns a destructive flow into a reversible one without a blocking confirm dialog.
toast("Event has been created", {
  action: {
    label: "Undo",
    onClick: () => restoreEvent(),
  },
})
Promise
toast.promise follows an async operation through its lifecycle: it shows the loading message with the wrapper's spinning icon, then swaps to the success or error message in place when the promise settles. success and error accept a function that receives the resolved value or the rejection reason.
toast.promise(saveEvent(), {
  loading: "Saving event…",
  success: (data) => `${data.name} has been created`,
  error: "Could not save the event",
})
Rich colors
By default every toast uses the neutral popover surface. richColors — per toast, or on the Toaster for all of them — tints the surface with Sonner's built-in green/red/amber/blue palette for louder success and error feedback.
toast.success("Changes saved", { richColors: true })
toast.error("Something went wrong", { richColors: true })
Duration and dismissal
Toasts auto-dismiss after 4 seconds. Raise duration per toast — or set it to Infinity with closeButton for messages that must be acknowledged — and call toast.dismiss() to clear one toast by id, or all of them with no argument.
toast("This toast stays until you close it", {
  duration: Infinity,
  closeButton: true,
})

toast.dismiss() // dismiss all
Accessibility
Sonner renders toasts in an ARIA live region, so screen readers announce new toasts without moving focus, and the ⌥/Alt + T hotkey focuses the region so keyboard users can reach a toast's action or close button; timers pause while a toast is hovered or focused. Toast types pair an icon with their color, so state never rides on color alone. Still, a toast is transient by nature — give toasts that carry an action a longer duration and a close button, and never make a toast the only place an important outcome is reported.
toast("Event deleted", {
  action: { label: "Undo", onClick: () => restoreEvent() },
  duration: 10000, // more time to reach the action
  closeButton: true,
})
API Reference
The package exports a single part: Toaster, a configured sonner Toaster that resolves its theme from next-themes, swaps the default icons for Phosphor ones and maps the toast surface to the popover tokens. Everything else is the toast() function from sonner. Toaster accepts every upstream prop; per-toast options override the Toaster-level defaults. See the Sonner documentation for the full API.
PartPropDefault
Toasterposition?: "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right""bottom-right"
Toasterduration?: number4000
ToasterrichColors?: boolean, closeButton?: boolean, invert?: booleanfalse
Toasterexpand?: booleanfalse
ToastervisibleToasts?: number3
Toasterdir?: "ltr" | "rtl" | "auto""auto"
toast()description?: ReactNode, action?: { label, onClick }—
toast()duration?, position?, richColors?, closeButton?Toaster
toast()id?: string | numberauto
toast("Event has been created.")
toast.success("Changes saved")
toast.info("A new version is available")
toast.warning("Storage is almost full")
toast.error("Something went wrong")
toast.loading("Uploading…")
toast.promise(promise, { loading, success, error })
toast.custom((id) => <MyToast id={id} />)
toast.dismiss(id) // no id → dismiss all

Tokens used

5 design tokens consumed by the Sonner component.

  • --popover

    Popover

    --normal-bg: var(--popover)
  • --popover-foreground

    Popover Foreground

    --normal-text: var(--popover-foreground)
  • --border

    Border

    --normal-border: var(--border)
  • --radius

    Radius (10px)

    --border-radius: var(--radius)
  • --spacing

    Spacing unit (4px)

    size-4