{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "sonner",
"title": "Sonner",
"description": "A toast notification surface wired to next-themes, with NSWDS status icons.",
"dependencies": [
"clsx",
"next-themes",
"sonner",
"tailwind-merge"
],
"registryDependencies": [
"https://ui.digital.nsw.gov.au/registry/r/theme.json",
"https://ui.digital.nsw.gov.au/registry/r/spinner.json",
"https://ui.digital.nsw.gov.au/registry/r/icons.json"
],
"files": [
{
"path": "src/components/sonner.tsx",
"content": "'use client'\n\nimport { useTheme } from 'next-themes'\nimport * as React from 'react'\nimport { Toaster as Sonner, type ToasterProps } from 'sonner'\n\nimport { Spinner } from '@/components/spinner'\nimport { IconCheckCircle } from '@/icons/check-circle'\nimport { IconError } from '@/icons/error'\nimport { IconInfo } from '@/icons/info'\nimport { IconWarning } from '@/icons/warning'\nimport { cn } from '@/lib/utils'\n\n/**\n * Binds sonner's own theming variables to the NSW semantic tokens.\n *\n * All four names below are sonner's public CSS API — three `--normal-*` colour\n * variables plus `--border-radius`, which is a separate name rather than part\n * of that family. Sonner's own stylesheet reads each of them.\n *\n * They are set ONLY here: none of the four is defined in the compiled\n * `dist/styles.css`, so this object is the single point at which the design\n * system's tokens reach a toast. Losing it silently drops every toast back to\n * sonner's built-in palette, which is why `style` is merged below rather than\n * overwritten.\n */\nconst tokenBridge = {\n '--normal-bg': 'var(--popover)',\n '--normal-text': 'var(--popover-foreground)',\n '--normal-border': 'var(--border)',\n '--border-radius': 'var(--radius)',\n} as React.CSSProperties\n\n/**\n * Toast host. Mount once, near the root.\n *\n * Every prop this component sets a default for is MERGED with the consumer's\n * value rather than replaced. Spreading `{...props}` last — the shadcn default\n * this was scaffolded from — meant a consumer passing `style` for something\n * unrelated (a z-index, an offset) silently discarded the whole token bridge\n * above, and passing one `icons` entry discarded the other four. Object spread\n * replaces keys wholesale; it does not deep-merge.\n */\nconst Toaster = ({ className, style, icons, toastOptions, ...props }: ToasterProps) => {\n const { theme = 'system' } = useTheme()\n\n return (\n ,\n info: ,\n warning: ,\n error: ,\n loading: ,\n ...icons,\n }}\n style={{ ...tokenBridge, ...style }}\n // Two levels of merge, not one: a consumer's `toastOptions` must not drop\n // our `classNames`, and their `classNames` must not drop `toast`.\n toastOptions={{\n ...toastOptions,\n classNames: {\n toast: 'cn-toast',\n ...toastOptions?.classNames,\n },\n }}\n {...props}\n />\n )\n}\n\nexport { Toaster }\n",
"type": "registry:ui",
"target": "components/sonner.tsx"
},
{
"path": "src/lib/utils.ts",
"content": "import { clsx, type ClassValue } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n",
"type": "registry:lib",
"target": "lib/utils.ts"
}
],
"type": "registry:ui",
"meta": {
"nswdsVersion": "5.1.0"
}
}