{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "announcement-banner",
  "title": "Announcement Banner",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-react/blocks/announcement-banner/AnnouncementBanner.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport { ArrowRight, ChevronLeft, ChevronRight, Megaphone, Terminal, X } from 'lucide-react'\nimport { Badge } from '@/components/ui/badge'\nimport { cn } from '@/lib/utils'\n\nexport interface AnnouncementItem {\n  id: string\n  category: 'release' | 'security' | 'maintenance' | 'feature'\n  badgeText: string\n  title: string\n  actionLabel: string\n  actionUrl?: string\n  cliSnippet?: string\n}\n\nexport interface AnnouncementBannerProps {\n  variant?: 'floating-pill' | 'top-bar' | 'interactive-ticker'\n  autoplay?: boolean\n  className?: string\n}\n\nconst announcements: AnnouncementItem[] = [\n  {\n    id: 'ann-1',\n    category: 'release',\n    badgeText: 'v2.4.0 Release',\n    title: 'Tailwind CSS v4 OKLCH tokens & 281+ production workbenches are live.',\n    actionLabel: 'Read Changelog',\n    actionUrl: 'https://uipkge.dev/changelog',\n    cliSnippet: 'npx shadcn-vue@latest add https://uipkge.dev/r/vue/init.json',\n  },\n  {\n    id: 'ann-2',\n    category: 'feature',\n    badgeText: 'Dual-Framework',\n    title: '100% Vue 3.5 & React 19 component parity achieved with zero runtime CSS.',\n    actionLabel: 'Explore Parity Matrix',\n    actionUrl: 'https://uipkge.dev/components',\n    cliSnippet: 'npx shadcn@latest add https://uipkge.dev/r/react/init.json',\n  },\n  {\n    id: 'ann-3',\n    category: 'security',\n    badgeText: 'Security Notice',\n    title: 'Upstream Reka UI & Radix dependency security patches verified and updated.',\n    actionLabel: 'Security Advisory',\n    actionUrl: 'https://uipkge.dev/security',\n  },\n]\n\nexport function AnnouncementBanner({\n  variant = 'interactive-ticker',\n  autoplay = true,\n  className,\n}: AnnouncementBannerProps) {\n  const [currentIndex, setCurrentIndex] = React.useState(0)\n  const [dismissed, setDismissed] = React.useState(false)\n  const [isPaused, setIsPaused] = React.useState(false)\n  const [copiedCli, setCopiedCli] = React.useState(false)\n\n  React.useEffect(() => {\n    if (!autoplay || isPaused) return\n    const timer = setInterval(() => {\n      setCurrentIndex((prev) => (prev + 1) % announcements.length)\n    }, 6000)\n    return () => clearInterval(timer)\n  }, [autoplay, isPaused])\n\n  const nextAnnouncement = () => {\n    setCurrentIndex((prev) => (prev + 1) % announcements.length)\n  }\n\n  const prevAnnouncement = () => {\n    setCurrentIndex((prev) => (prev - 1 + announcements.length) % announcements.length)\n  }\n\n  const copyCliSnippet = (snippet?: string) => {\n    if (!snippet) return\n    navigator.clipboard.writeText(snippet)\n    setCopiedCli(true)\n    setTimeout(() => setCopiedCli(false), 2000)\n  }\n\n  if (dismissed) return null\n\n  const current = announcements[currentIndex]\n\n  return (\n    <div\n      data-slot=\"announcement-banner\"\n      data-variant={variant}\n      role=\"region\"\n      aria-label=\"Announcement\"\n      className={cn('relative z-50 transition-colors duration-200', className)}\n      onMouseEnter={() => setIsPaused(true)}\n      onMouseLeave={() => setIsPaused(false)}\n    >\n      {/* 1. Interactive Ticker Mode (Default) */}\n      {variant === 'interactive-ticker' && (\n        <div className=\"bg-muted/70 border-border text-foreground flex items-center justify-between gap-3 overflow-hidden border-b px-4 py-2 text-xs shadow-xs backdrop-blur-md\">\n          <div className=\"mx-auto flex w-full max-w-6xl items-center justify-between gap-4\">\n            {/* Left: Status Beacon & Item Indicator */}\n            <div className=\"flex shrink-0 items-center gap-3\">\n              <div className=\"text-muted-foreground flex items-center gap-1.5 font-mono text-xs\">\n                <span className=\"bg-primary size-2 animate-pulse rounded-full\" />\n                <span className=\"text-foreground font-semibold\">Live Dispatch</span>\n              </div>\n\n              <Badge\n                variant=\"secondary\"\n                className={cn(\n                  'px-2 py-0.5 font-mono text-xs',\n                  current.category === 'release' && 'bg-primary/10 text-primary border-primary/20',\n                  current.category === 'security' && 'border-warning/20 bg-warning/10 text-warning',\n                  current.category === 'feature' && 'border-success/20 bg-success/10 text-success',\n                )}\n              >\n                {current.badgeText}\n              </Badge>\n            </div>\n\n            {/* Center: Dynamic Headline & Action Link */}\n            <div className=\"flex flex-1 items-center justify-center gap-2 truncate\">\n              <p className=\"text-foreground truncate font-medium\">{current.title}</p>\n              {current.actionUrl && (\n                <a\n                  href={current.actionUrl}\n                  className=\"text-primary hidden shrink-0 items-center gap-1 font-semibold hover:underline sm:inline-flex\"\n                >\n                  {current.actionLabel} <ArrowRight className=\"size-3\" />\n                </a>\n              )}\n            </div>\n\n            {/* Right: Ticker Navigation & CLI / Dismiss */}\n            <div className=\"flex shrink-0 items-center gap-2\">\n              {current.cliSnippet && (\n                <button\n                  type=\"button\"\n                  className=\"bg-background border-border text-muted-foreground hover:text-foreground hidden items-center gap-1.5 rounded border px-2 py-0.5 font-mono text-xs transition-colors md:inline-flex\"\n                  onClick={() => copyCliSnippet(current.cliSnippet)}\n                >\n                  <Terminal className=\"text-primary size-3\" />\n                  <span>{copiedCli ? 'Copied CLI!' : 'Copy CLI'}</span>\n                </button>\n              )}\n\n              <div className=\"border-border bg-background flex items-center overflow-hidden rounded-md border\">\n                <button\n                  type=\"button\"\n                  className=\"hover:bg-muted text-muted-foreground hover:text-foreground p-1 transition-colors\"\n                  aria-label=\"Previous announcement\"\n                  onClick={prevAnnouncement}\n                >\n                  <ChevronLeft className=\"size-3.5\" />\n                </button>\n                <span className=\"text-muted-foreground px-1.5 font-mono text-xs\">\n                  {currentIndex + 1}/{announcements.length}\n                </span>\n                <button\n                  type=\"button\"\n                  className=\"hover:bg-muted text-muted-foreground hover:text-foreground p-1 transition-colors\"\n                  aria-label=\"Next announcement\"\n                  onClick={nextAnnouncement}\n                >\n                  <ChevronRight className=\"size-3.5\" />\n                </button>\n              </div>\n\n              <button\n                type=\"button\"\n                aria-label=\"Dismiss banner\"\n                className=\"text-muted-foreground hover:text-foreground hover:bg-muted rounded-md p-1 transition-colors\"\n                onClick={() => setDismissed(true)}\n              >\n                <X className=\"size-3.5\" />\n              </button>\n            </div>\n          </div>\n        </div>\n      )}\n\n      {/* 2. Floating Pill Mode */}\n      {variant === 'floating-pill' && (\n        <div className=\"mx-auto w-fit max-w-2xl px-4 py-2\">\n          <div className=\"bg-card/90 border-border text-foreground hover:border-primary/40 flex items-center gap-3 rounded-full border py-1.5 pr-2 pl-3 text-xs shadow-md backdrop-blur-md transition-colors\">\n            <Badge\n              variant=\"secondary\"\n              className=\"bg-primary/10 text-primary border-primary/20 px-2 py-0.5 font-mono text-xs\"\n            >\n              {current.badgeText}\n            </Badge>\n            <p className=\"text-foreground max-w-sm truncate font-medium\">{current.title}</p>\n            {current.actionUrl && (\n              <a\n                href={current.actionUrl}\n                className=\"text-primary inline-flex shrink-0 items-center gap-1 font-semibold hover:underline\"\n              >\n                {current.actionLabel} <ArrowRight className=\"size-3\" />\n              </a>\n            )}\n            <button\n              type=\"button\"\n              aria-label=\"Dismiss announcement\"\n              className=\"text-muted-foreground hover:text-foreground hover:bg-muted rounded-full p-1 transition-colors\"\n              onClick={() => setDismissed(true)}\n            >\n              <X className=\"size-3.5\" />\n            </button>\n          </div>\n        </div>\n      )}\n\n      {/* 3. Top Solid Bar Mode */}\n      {variant === 'top-bar' && (\n        <div className=\"bg-primary text-primary-foreground flex items-center justify-between gap-3 px-4 py-2 text-xs shadow-xs\">\n          <div className=\"mx-auto flex w-full max-w-6xl items-center justify-between gap-4\">\n            <div className=\"flex items-center gap-2 truncate\">\n              <Megaphone className=\"size-4 shrink-0\" />\n              <span className=\"font-mono font-bold\">[{current.badgeText}]</span>\n              <span className=\"truncate\">{current.title}</span>\n            </div>\n            <div className=\"flex shrink-0 items-center gap-3\">\n              {current.actionUrl && (\n                <a\n                  href={current.actionUrl}\n                  className=\"inline-flex items-center gap-1 font-semibold underline-offset-4 hover:underline\"\n                >\n                  {current.actionLabel} <ArrowRight className=\"size-3\" />\n                </a>\n              )}\n              <button\n                type=\"button\"\n                aria-label=\"Dismiss banner\"\n                className=\"text-primary-foreground/80 hover:text-primary-foreground hover:bg-primary-foreground/15 rounded p-1 transition-colors\"\n                onClick={() => setDismissed(true)}\n              >\n                <X className=\"size-3.5\" />\n              </button>\n            </div>\n          </div>\n        </div>\n      )}\n    </div>\n  )\n}\nexport default AnnouncementBanner\n",
      "type": "registry:block",
      "target": "~/components/blocks/AnnouncementBanner.tsx"
    }
  ],
  "dependencies": [
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/react/badge.json"
  ],
  "description": "Interactive release, security, and maintenance broadcast center with rotating live ticker, CLI copy button, and floating pill / top-bar modes.",
  "categories": [
    "marketing"
  ],
  "deprecated": true,
  "replacedBy": "announcement"
}