{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "banner",
  "title": "Banner",
  "description": "A page-top announcement bar with info, accent and destructive variants, a real action link and consumer-owned dismissal.",
  "dependencies": [
    "class-variance-authority",
    "lucide-react"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/registry/ui/banner.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { ArrowRight, Info, Sparkles, TriangleAlert, X } from \"lucide-react\"\nimport { cn } from \"@/lib/utils\"\n\nconst bannerVariants = cva(\n  \"relative flex w-full items-center gap-3 border px-4 py-3 text-sm\",\n  {\n    variants: {\n      variant: {\n        info: \"border-transparent bg-muted text-foreground\",\n        accent: \"border-primary/30 bg-primary/10 text-foreground\",\n        destructive: \"border-transparent bg-destructive/10 text-destructive\",\n      },\n    },\n    defaultVariants: { variant: \"info\" },\n  },\n)\n\ntype BannerVariant = NonNullable<VariantProps<typeof bannerVariants>[\"variant\"]>\n\nconst DEFAULT_ICONS: Record<BannerVariant, React.ReactNode> = {\n  info: <Info className=\"size-4\" />,\n  accent: <Sparkles className=\"size-4\" />,\n  destructive: <TriangleAlert className=\"size-4\" />,\n}\n\nexport interface BannerProps\n  extends React.HTMLAttributes<HTMLDivElement>,\n    VariantProps<typeof bannerVariants> {\n  /** Overrides the per-variant default icon; pass null to render no icon. */\n  icon?: React.ReactNode\n  /** Renders a real anchor with a trailing arrow — href stays middle-clickable. */\n  action?: { label: string; href: string }\n  /**\n   * When provided, a dismiss button (aria-label \"Dismiss\") appears. The banner\n   * never hides itself — visibility is consumer-owned (conditional render).\n   */\n  onDismiss?: () => void\n}\n\nexport const Banner = React.forwardRef<HTMLDivElement, BannerProps>(\n  ({ variant, icon, action, onDismiss, className, children, ...props }, ref) => {\n    const resolved: BannerVariant = variant ?? \"info\"\n\n    return (\n      <div\n        className={cn(bannerVariants({ variant: resolved }), className)}\n        ref={ref}\n        role={resolved === \"destructive\" ? \"alert\" : \"status\"}\n        {...props}\n      >\n        {icon !== null && (\n          <span\n            aria-hidden=\"true\"\n            className={cn(\"shrink-0\", resolved === \"accent\" && \"text-primary\")}\n          >\n            {icon === undefined ? DEFAULT_ICONS[resolved] : icon}\n          </span>\n        )}\n\n        <span className=\"min-w-0 flex-1\">{children}</span>\n\n        {action && (\n          <a\n            className={cn(\n              \"inline-flex shrink-0 items-center gap-1 whitespace-nowrap rounded-sm font-medium underline-offset-4 hover:underline\",\n              \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n              resolved === \"accent\" && \"text-primary\",\n            )}\n            href={action.href}\n          >\n            {action.label}\n            <ArrowRight aria-hidden=\"true\" className=\"size-3.5\" />\n          </a>\n        )}\n\n        {onDismiss && (\n          <button\n            aria-label=\"Dismiss\"\n            className=\"-mr-1 shrink-0 cursor-pointer rounded-md p-1 opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n            onClick={onDismiss}\n            type=\"button\"\n          >\n            <X aria-hidden=\"true\" className=\"size-4\" />\n          </button>\n        )}\n      </div>\n    )\n  },\n)\n\nBanner.displayName = \"Banner\"\n\nexport default Banner\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}