{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toast",
  "type": "registry:ui",
  "title": "Toast",
  "description": "Floating notification pill fixed below the header, for app-level messages and background work.",
  "categories": [
    "overlays"
  ],
  "registryDependencies": [
    "https://whiskeyjack.net/r/utils.json"
  ],
  "files": [
    {
      "path": "components/ui/toast.tsx",
      "type": "registry:ui",
      "target": "components/ui/toast.tsx",
      "content": "import * as React from 'react'\nimport { cn } from '@/lib/utils'\n\nexport interface ToastProps extends React.HTMLAttributes<HTMLDivElement> {\n  /** Drives the fade/slide transition. The caller owns the state and any\n   *  auto-dismiss timing -- the component never dismisses itself. */\n  open: boolean\n  /** Optional leading icon element (e.g. a 16px Phosphor icon). */\n  icon?: React.ReactNode\n  /** Optional trailing action (e.g. a small accent Button). Its presence\n   *  makes the toast interactive (pointer events enabled). */\n  action?: React.ReactNode\n  /** When provided, renders an X dismiss button and enables pointer events. */\n  onDismiss?: () => void\n  /** aria-label for the dismiss button. */\n  dismissLabel?: string\n  /**\n   * When `true`, the toast is announced immediately via `role=\"alert\"` +\n   * `aria-live=\"assertive\"` -- use for time-sensitive or error messages.\n   * Default (`false`) uses `aria-live=\"polite\"`, which waits for the user\n   * to finish their current interaction before announcing.\n   */\n  assertive?: boolean\n  /**\n   * Work is in flight. Renders the design system's spinner in place of `icon`\n   * and sets `aria-busy`, the same word `ProgressBar` uses for the same state.\n   *\n   * The spinner is drawn from a border rather than an icon, because the\n   * package ships no icon library. The LABEL carries the meaning: under\n   * reduced motion the global backstop leaves the circle effectively still,\n   * so a toast whose only cue is the spinner says nothing there.\n   */\n  busy?: boolean\n  /**\n   * A readout that changes as the work proceeds -- a count, a percentage --\n   * rendered after the message and **hidden from assistive tech**.\n   *\n   * The pill is a live region, so a number ticking several times a second\n   * would be read out several times a second, talking over everything else on\n   * the page. Hidden, the message announces once and the readout stays a\n   * visual one. Digits are tabular so the pill does not resize on every tick.\n   */\n  detail?: React.ReactNode\n}\n\n/**\n * Floating notification pill, fixed and centered below the app header.\n * Frosted-surface chrome (same recipe as BottomNav and the surface\n * ActionPill) so it reads as part of the header family.\n *\n * Use for app-level, out-of-flow notifications: transient state hints\n * (auto-dismissed by the caller, e.g. an armed destructive button) or\n * persistent action-carrying notices (e.g. \"update available\" with a\n * download button + dismiss). For context-bound inline status and error\n * boxes inside forms or settings sections, use `Notice` instead.\n *\n * Purely informational toasts (no `action`, no `onDismiss`) are\n * pointer-events-none so they never intercept clicks. Keep messages to a\n * single short sentence -- the pill shape does not suit multiline text.\n *\n * For background work that resolves on its own -- an index building, a library\n * scanning -- pass `busy` with a `detail` readout and neither an action nor a\n * dismiss: there is nothing to confirm and nothing to cancel, and a dismiss\n * would only offer to hide a fact. Staying non-interactive also keeps the pill\n * from swallowing clicks meant for what is underneath it.\n */\nconst Toast = React.forwardRef<HTMLDivElement, ToastProps>(\n  (\n    {\n      open,\n      icon,\n      action,\n      onDismiss,\n      dismissLabel = 'Dismiss',\n      assertive = false,\n      busy = false,\n      detail,\n      className,\n      children,\n      ...props\n    },\n    ref,\n  ) => {\n    const interactive = Boolean(action || onDismiss)\n    return (\n      <div\n        ref={ref}\n        // `role=\"alert\"` carries its own implicit assertive live region; the\n        // explicit aria-live pairs with it for the screen readers that read\n        // only one of the two.\n        role={assertive ? 'alert' : undefined}\n        aria-live={assertive ? 'assertive' : 'polite'}\n        {...(busy ? { 'aria-busy': true } : {})}\n        className={cn(\n          'fixed left-1/2 -translate-x-1/2 z-40',\n          'top-[calc(env(safe-area-inset-top)+0.75rem)] md:top-20',\n          'flex items-center gap-2 px-4 py-2 rounded-full',\n          'max-w-[calc(100vw-2rem)]',\n          'wj-frost',\n          'border border-[var(--color-border-light)] dark:border-[var(--color-border-dark)]',\n          'shadow-lg text-sm',\n          'text-[var(--color-text-primary-light)] dark:text-[var(--color-text-primary-dark)]',\n          'transition-all duration-300',\n          open ? 'opacity-100 translate-y-0' : 'opacity-0 -translate-y-2',\n          open && interactive ? 'pointer-events-auto' : 'pointer-events-none',\n          className,\n        )}\n        {...props}\n      >\n        {busy ? (\n          <span\n            aria-hidden\n            className=\"block h-4 w-4 shrink-0 animate-spin rounded-full border-2 border-[var(--color-accent-500)] border-t-transparent\"\n          />\n        ) : (\n          icon\n        )}\n        <span className=\"min-w-0\">{children}</span>\n        {detail != null && (\n          <span aria-hidden className=\"shrink-0 tabular-nums\">\n            {detail}\n          </span>\n        )}\n        {action}\n        {onDismiss && (\n          <button\n            type=\"button\"\n            onClick={onDismiss}\n            aria-label={dismissLabel}\n            title={dismissLabel}\n            className=\"wj-focus-ring p-1 -me-1 rounded-full text-[var(--color-text-muted-light)] dark:text-[var(--color-text-muted-dark)] hover:bg-[var(--color-warm-200)] dark:hover:bg-[var(--color-surface-muted-dark)] transition-colors\"\n          >\n            {/* Inline X glyph: the DS stays icon-library-free (same documented\n                exception as the Select caret). */}\n            <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n              <path\n                d=\"M3.5 3.5l9 9m0-9l-9 9\"\n                stroke=\"currentColor\"\n                strokeWidth=\"1.8\"\n                strokeLinecap=\"round\"\n              />\n            </svg>\n          </button>\n        )}\n      </div>\n    )\n  },\n)\nToast.displayName = 'Toast'\n\nexport { Toast }\n"
    }
  ],
  "docs": "Toast is for app-level notifications that float above the page: an armed-state hint, an update-available message with an action, or background work still running. For a message bound to a specific surface, use Notice. The caller owns `open` and any auto-dismiss timing. Keep messages to one short sentence. For work that resolves on its own, pass `busy` (draws the spinner, sets aria-busy) with a `detail` readout and no action or dismiss -- `detail` is hidden from assistive tech because the pill is a live region and a fast-changing count would otherwise be announced on every tick. `assertive` upgrades the announcement to role=alert for a message whose moment passes.",
  "meta": {
    "group": "overlays",
    "related": [
      "notice",
      "badge"
    ],
    "exports": [
      "Toast"
    ],
    "siteSlug": "toast"
  }
}
