Primitives
Toast (Sonner)
Toast notifications themed to the okibi tokens.
● LIVE
[ interactive — use the trigger to open ]
"use client";import { toast } from "sonner";import { useTheme } from "next-themes";import { Button } from "@/registry/okibi/ui/button";import { Toaster } from "@/registry/okibi/ui/sonner";export default function SonnerDemo() { // Follow the docs theme so toasts match the active (light/dark) mode. const { resolvedTheme } = useTheme(); return ( <> <Button variant="outline-tech" onClick={() => toast.success("SEC // 0x4A2 — DEPLOYED", { description: "Perimeter ICE online.", }) } > Deploy </Button> <Toaster theme={resolvedTheme === "light" ? "light" : "dark"} /> </> );}npx shadcn@latest add https://okibi.cndr.dev/r/sonner.jsonInstallation
Install the theme first, then add the component:
npx shadcn@latest add https://okibi.cndr.dev/r/sonner.jsonInstall the required dependencies:
npm install lucide-react sonnerCopy the source from the Code tab above into components/ui/sonner.tsx. It uses the cn helper and the okibi theme tokens — install the theme first if you haven't.
Usage
import { toast } from "sonner"
import { Toaster } from "@/components/ui/sonner"
// Mount once near the root:
<Toaster />
// Then fire toasts from anywhere:
toast.success("SEC // 0x4A2 — PURGED")Anatomy
// Mount once near the app root:
<Toaster />
// Then fire toasts from anywhere:
toast.success("SEC // 0x4A2 — PURGED")Examples
Types
toast.success, toast.error, toast.warning, and toast.info each carry their own semantic accent; the bare toast is neutral.
● LIVE
[ interactive — use the trigger to open ]
"use client";import { toast } from "sonner";import { useTheme } from "next-themes";import { Button } from "@/registry/okibi/ui/button";import { Toaster } from "@/registry/okibi/ui/sonner";export default function SonnerTypesDemo() { // Follow the docs theme so toasts match the active (light/dark) mode. const { resolvedTheme } = useTheme(); return ( <> <div className="flex flex-wrap justify-center gap-2"> <Button variant="outline-tech" onClick={() => toast.success("SEC // 0x4A2 — DEPLOYED", { description: "Perimeter ICE online.", }) } > Success </Button> <Button variant="outline-tech" onClick={() => toast.error("CH-02 — LINK LOST", { description: "Carrier dropped mid-trace.", }) } > Error </Button> <Button variant="outline-tech" onClick={() => toast.warning("RELAY // 0x0C9 — DRIFT", { description: "Clock skew exceeds tolerance.", }) } > Warning </Button> <Button variant="outline-tech" onClick={() => toast.info("SIGNAL // CALM", { description: "No anomalies on the grid.", }) } > Info </Button> <Button variant="outline-tech" onClick={() => toast("PING // 0x4A2 acknowledged.")} > Default </Button> </div> <Toaster theme={resolvedTheme === "light" ? "light" : "dark"} /> </> );}With action
Attach an action button to a toast for an inline undo or retry.
● LIVE
[ interactive — use the trigger to open ]
"use client";import { toast } from "sonner";import { useTheme } from "next-themes";import { Button } from "@/registry/okibi/ui/button";import { Toaster } from "@/registry/okibi/ui/sonner";export default function SonnerActionDemo() { // Follow the docs theme so toasts match the active (light/dark) mode. const { resolvedTheme } = useTheme(); return ( <> <Button variant="outline-tech" onClick={() => toast("SEC // 0x4A2 — PURGED", { description: "Log buffer cleared from the relay.", action: { label: "Undo", onClick: () => {}, }, }) } > Purge Logs </Button> <Toaster theme={resolvedTheme === "light" ? "light" : "dark"} /> </> );}Promise
toast.promise tracks an async task, swapping loading → success / error copy as it settles.
● LIVE
[ interactive — use the trigger to open ]
"use client";import { toast } from "sonner";import { useTheme } from "next-themes";import { Button } from "@/registry/okibi/ui/button";import { Toaster } from "@/registry/okibi/ui/sonner";export default function SonnerPromiseDemo() { // Follow the docs theme so toasts match the active (light/dark) mode. const { resolvedTheme } = useTheme(); return ( <> <Button variant="outline-tech" onClick={() => toast.promise(new Promise((res) => setTimeout(res, 1800)), { loading: "Tracing route to SEC // 0x4A2...", success: "Trace locked — grid reference acquired.", error: "Trace timed out — carrier unreachable.", }) } > Trace Route </Button> <Toaster theme={resolvedTheme === "light" ? "light" : "dark"} /> </> );}Accessibility
- Sonner owns the
aria-liveregion and toast roles — mountToasteronce and it announces new toasts automatically. - Toasts are transient and not a replacement for blocking errors — surface anything that must be acted on in-flow.
- Semantic type icons are swapped to okibi glyphs tinted with the per-mode
*-sparktokens so success / error / warning / info meet WCAG AA on the popover surface. - The surface stays neutral (popover bg/fg,
border-strong); color rides the icon, not the whole toast. - Pass
closeButtonor adurationfor important toasts so they don't auto-dismiss before they can be read.
Guidelines
Do
- Mount a single
Toasternear the root; fire toasts withtoast()from anywhere. - Use the semantic helpers (
toast.success,toast.error, …) so the on-brand*-sparkicon is applied. - Give a toast a
closeButtonor longerdurationwhen the message matters and shouldn't auto-dismiss.
Don't
- Don't pass
richColorsexpecting the okibi palette — the surface is deliberately neutral; tint rides the type icon. - Don't use a toast for errors the user must resolve — keep blocking failures inline.
- Don't mount more than one
Toaster.