{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"banner","type":"registry:ui","title":"Banner","description":"A strip across the page for news that concerns everyone: a launch, a sale, planned downtime.","dependencies":["lucide-react"],"registryDependencies":["https://dashral.com/r/utils.json","https://dashral.com/r/lib-locale.json"],"files":[{"path":"registry/dashral/banner.tsx","type":"registry:file","target":"components/dashral/banner.tsx","content":"\"use client\"\n\nimport * as React from \"react\"\nimport { XIcon } from \"lucide-react\"\n\nimport { useLocale } from \"./lib/locale\"\nimport { cn } from \"./lib/utils\"\n\ntype BannerVariant = \"solid\" | \"soft\" | \"outline\"\ntype BannerColor = \"neutral\" | \"primary\" | \"info\" | \"success\" | \"warning\" | \"destructive\" | \"highlight\"\n\n// Every class is written out in full so Tailwind finds it. The description takes the text color, a little softer.\nconst tones: Record<BannerVariant, Record<BannerColor, string>> = {\n  solid: {\n    neutral: \"border-transparent bg-foreground text-background **:data-[slot=banner-body]:text-background/75\",\n    primary: \"border-transparent bg-primary text-primary-foreground **:data-[slot=banner-body]:text-primary-foreground/80\",\n    info: \"border-transparent bg-info text-info-foreground **:data-[slot=banner-body]:text-info-foreground/80\",\n    success: \"border-transparent bg-success text-success-foreground **:data-[slot=banner-body]:text-success-foreground/80\",\n    warning: \"border-transparent bg-warning text-warning-foreground **:data-[slot=banner-body]:text-warning-foreground/80\",\n    destructive: \"border-transparent bg-destructive text-destructive-foreground **:data-[slot=banner-body]:text-destructive-foreground/80\",\n    highlight: \"border-transparent bg-highlight text-highlight-foreground **:data-[slot=banner-body]:text-highlight-foreground/80\",\n  },\n  soft: {\n    neutral: \"bg-muted text-foreground **:data-[slot=banner-body]:text-muted-foreground\",\n    primary: \"border-primary/20 bg-primary/8 text-primary-text **:data-[slot=banner-body]:text-primary-text/85\",\n    info: \"border-info/20 bg-info/8 text-info-text **:data-[slot=banner-body]:text-info-text/85\",\n    success: \"border-success/20 bg-success/8 text-success-text **:data-[slot=banner-body]:text-success-text/85\",\n    warning: \"border-warning/30 bg-warning/10 text-warning-text **:data-[slot=banner-body]:text-warning-text/85\",\n    destructive: \"border-destructive/20 bg-destructive/8 text-destructive-text **:data-[slot=banner-body]:text-destructive-text/85\",\n    highlight: \"border-highlight/20 bg-highlight/8 text-highlight-text **:data-[slot=banner-body]:text-highlight-text/85\",\n  },\n  outline: {\n    neutral: \"bg-card text-card-foreground **:data-[slot=banner-body]:text-muted-foreground\",\n    primary: \"border-primary/40 bg-card text-primary-text **:data-[slot=banner-body]:text-muted-foreground\",\n    info: \"border-info/40 bg-card text-info-text **:data-[slot=banner-body]:text-muted-foreground\",\n    success: \"border-success/40 bg-card text-success-text **:data-[slot=banner-body]:text-muted-foreground\",\n    warning: \"border-warning/50 bg-card text-warning-text **:data-[slot=banner-body]:text-muted-foreground\",\n    destructive: \"border-destructive/40 bg-card text-destructive-text **:data-[slot=banner-body]:text-muted-foreground\",\n    highlight: \"border-highlight/40 bg-card text-highlight-text **:data-[slot=banner-body]:text-muted-foreground\",\n  },\n}\n\ntype BannerProps = Omit<React.ComponentProps<\"div\">, \"title\" | \"color\"> & {\n  /** The message, in bold: \"Dashral 1.1 is out\". */\n  title?: React.ReactNode\n  /** After the title, softer: \"Six new components and a calendar.\" */\n  description?: React.ReactNode\n  /** Before the text, e.g. an icon or a \"New\" badge. */\n  icon?: React.ReactNode\n  /** After the text, e.g. a link (\"Read the notes →\") or a small button. */\n  action?: React.ReactNode\n  /** How much it stands out: solid (default), soft or outline. */\n  variant?: BannerVariant\n  /** What it means. Neutral (ink) by default. */\n  color?: BannerColor\n  /** Its text in the middle (default), or at the start with the close button at the far end. */\n  align?: \"center\" | \"start\"\n  /** A rounded card with a shadow and a margin, floating over the page, instead of a full-width bar. */\n  floating?: boolean\n  /** Shows a close button, and is called when it's pressed: hide the banner and remember it, e.g. in a cookie. */\n  onClose?: () => void\n  classNames?: { title?: string; description?: string; action?: string }\n}\n\n/**\n * A strip across the top (or bottom) of a page for news that concerns everyone: a launch, a sale, planned downtime.\n * In one tag: a title and description, an icon, an action and a close button. On a phone the text wraps and the\n * close button stays at the end. For a message about one part of a page, use Alert.\n */\nfunction Banner({\n  title,\n  description,\n  icon,\n  action,\n  variant = \"solid\",\n  color = \"neutral\",\n  align = \"center\",\n  floating = false,\n  onClose,\n  className,\n  classNames,\n  children,\n  ...props\n}: BannerProps) {\n  const { t } = useLocale()\n  return (\n    <div\n      data-slot=\"banner\"\n      data-floating={floating || undefined}\n      className={cn(\n        \"flex w-full items-center gap-3 border-b px-4 py-2.5 text-sm\",\n        floating && \"rounded-lg border shadow-lg\",\n        tones[variant][color],\n        className\n      )}\n      {...props}\n    >\n      {/* Balances the close button, so centred text sits in the middle of the whole bar. */}\n      {onClose && align === \"center\" && <span aria-hidden className=\"size-6 shrink-0 max-sm:hidden\" />}\n      <div\n        className={cn(\n          \"flex min-w-0 flex-1 flex-wrap items-center gap-x-3 gap-y-1\",\n          align === \"center\" ? \"justify-center text-center\" : \"justify-start text-start\"\n        )}\n      >\n        {icon && <span className=\"flex shrink-0 items-center [&_svg]:size-4\">{icon}</span>}\n        {(title || description || children) && (\n          <div className=\"min-w-0 text-pretty\">\n            {title && <span className={cn(\"font-medium\", classNames?.title)}>{title}</span>}\n            {title && description && \" \"}\n            {description && (\n              <span data-slot=\"banner-body\" className={classNames?.description}>\n                {description}\n              </span>\n            )}\n            {children}\n          </div>\n        )}\n        {action && <span className={cn(\"flex shrink-0 items-center gap-2 font-medium\", classNames?.action)}>{action}</span>}\n      </div>\n      {onClose && (\n        <button\n          type=\"button\"\n          aria-label={t(\"dismiss\")}\n          onClick={onClose}\n          className=\"-me-1 flex size-6 shrink-0 items-center justify-center rounded-md opacity-70 outline-none transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-current/40 [&_svg]:size-4\"\n        >\n          <XIcon aria-hidden />\n        </button>\n      )}\n    </div>\n  )\n}\n\nexport { Banner, type BannerProps, type BannerVariant, type BannerColor }\n"}],"cssVars":{"theme":{"color-primary-text":"var(--primary-text, var(--primary))","color-destructive-foreground":"var(--destructive-foreground)","color-destructive-text":"var(--destructive-text)","color-info":"var(--info)","color-info-foreground":"var(--info-foreground)","color-info-text":"var(--info-text)","color-success":"var(--success)","color-success-foreground":"var(--success-foreground)","color-success-text":"var(--success-text)","color-warning":"var(--warning)","color-warning-foreground":"var(--warning-foreground)","color-warning-text":"var(--warning-text)","color-highlight":"var(--highlight)","color-highlight-foreground":"var(--highlight-foreground)","color-highlight-text":"var(--highlight-text)"},"light":{"destructive-foreground":"oklch(0.985 0 0)","destructive-text":"#bd2800","info":"oklch(0.5 0.134 242.749)","info-foreground":"oklch(0.985 0 0)","info-text":"oklch(0.443 0.11 240.79)","success":"oklch(0.508 0.118 165.612)","success-foreground":"oklch(0.985 0 0)","success-text":"oklch(0.432 0.095 166.913)","warning":"#f59200","warning-foreground":"oklch(0.3 0.07 60)","warning-text":"#964e00","highlight":"#b8008c","highlight-foreground":"oklch(0.985 0 0)","highlight-text":"#a3007b"},"dark":{"destructive-foreground":"oklch(0.2 0 0)","destructive-text":"#ff8a5c","info":"oklch(0.685 0.169 237.323)","info-foreground":"oklch(0.2 0 0)","info-text":"oklch(0.746 0.16 232.661)","success":"oklch(0.696 0.17 162.48)","success-foreground":"oklch(0.2 0.04 160)","success-text":"oklch(0.765 0.177 163.223)","warning":"#f59200","warning-foreground":"oklch(0.25 0.06 60)","warning-text":"#ffba42","highlight":"#ff2ecf","highlight-foreground":"oklch(0.2 0 0)","highlight-text":"#ff75df"}}}