{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "alert",
  "title": "Alert",
  "description": "An inline message in four severities, mapped onto the closest M3 container roles.",
  "dependencies": [
    "class-variance-authority@^0.7.1",
    "lucide-react@^1.33.0"
  ],
  "registryDependencies": [
    "https://guillermo-rebolledo.github.io/materialcn/r/icon.json",
    "https://guillermo-rebolledo.github.io/materialcn/r/materialcn-theme.json",
    "https://guillermo-rebolledo.github.io/materialcn/r/utils.json"
  ],
  "files": [
    {
      "path": "src/components/ui/alert.tsx",
      "content": "import { type VariantProps } from \"class-variance-authority\"\nimport {\n  CircleAlertIcon,\n  CircleCheckIcon,\n  InfoIcon,\n  TriangleAlertIcon,\n} from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { alertTextColumn, alertVariants } from \"./alert-variants\"\nimport { Icon } from \"./icon\"\nimport type { AlertSeverity } from \"./alert.types\"\n\n/**\n * The default glyph per severity. Colour alone cannot carry the role — roughly\n * one man in twelve cannot separate the warning and success containers — so the\n * icon is on by default rather than opt-in.\n */\nconst SEVERITY_ICONS = {\n  info: InfoIcon,\n  success: CircleCheckIcon,\n  warning: TriangleAlertIcon,\n  error: CircleAlertIcon,\n} as const\n\n/**\n * Whether the severity is worth interrupting for.\n *\n * `alert` is assertive: it cuts across whatever a screen reader is currently\n * saying. That is right for something that has gone wrong and wrong for a\n * confirmation, which is why it is derived from the severity rather than left\n * to every call site to get right.\n */\nconst URGENT: Record<AlertSeverity, boolean> = {\n  info: false,\n  success: false,\n  warning: true,\n  error: true,\n}\n\ntype AlertProps = Omit<React.ComponentProps<\"div\">, \"children\"> &\n  Omit<VariantProps<typeof alertVariants>, \"severity\"> & {\n    children?: React.ReactNode\n    severity?: AlertSeverity\n    /** Replace the default glyph, or pass `false` for no icon at all. */\n    icon?: React.ReactNode | false\n    /**\n     * Override how insistently it is announced. Defaults to assertive for\n     * warning and error, polite for informational and success.\n     */\n    urgent?: boolean\n  }\n\n/**\n * A message about a page or a region of one, that stays until it is dealt with.\n *\n * The snackbar covers the transient case. This is the persistent one — a\n * form-level error, a service notice, a warning above a destructive area.\n */\nfunction Alert({\n  children,\n  className,\n  icon,\n  severity = \"info\",\n  urgent,\n  ...props\n}: AlertProps) {\n  const Glyph = SEVERITY_ICONS[severity]\n  const assertive = urgent ?? URGENT[severity]\n\n  return (\n    <div\n      data-slot=\"alert\"\n      data-severity={severity}\n      role={assertive ? \"alert\" : \"status\"}\n      // `role` sets the politeness, but only for content present when the\n      // element mounts in some screen readers; the explicit pair is what makes\n      // a message swapped into an existing alert announce too.\n      aria-live={assertive ? \"assertive\" : \"polite\"}\n      aria-atomic\n      className={cn(alertVariants({ severity }), className)}\n      {...props}\n    >\n      {icon === false ? null : (\n        <span data-slot=\"alert-icon\" className=\"row-span-full\">\n          {icon ?? (\n            <Icon size=\"md\">\n              <Glyph />\n            </Icon>\n          )}\n        </span>\n      )}\n      {children}\n    </div>\n  )\n}\n\n/** Optional. Present so a longer message can be scanned rather than read. */\nfunction AlertTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"alert-title\"\n      className={cn(\n        \"text-m3-title-md\",\n        alertTextColumn,\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction AlertDescription({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"alert-description\"\n      className={cn(\n        \"text-m3-body-md [&_p:not(:last-child)]:mb-m3-sm\",\n        alertTextColumn,\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\n/**\n * Actions sit below the message and wrap, rather than being pinned to a corner.\n *\n * A corner-pinned action has to reserve its width from the text at every\n * viewport, and on a phone that leaves a column of two-word lines beside an\n * empty gutter. Below the text, the alert simply gets taller.\n */\nfunction AlertAction({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"alert-action\"\n      className={cn(\n        \"mt-m3-sm flex flex-wrap items-center gap-m3-sm\",\n        alertTextColumn,\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nexport { Alert, AlertAction, AlertDescription, AlertTitle, alertVariants }\nexport type { AlertProps }\n",
      "type": "registry:ui"
    },
    {
      "path": "src/components/ui/alert-variants.ts",
      "content": "/**\n * Variant definitions live beside the component rather than inside it — see\n * badge-variants.ts for why (React Fast Refresh boundaries).\n */\n\nimport { cva } from \"class-variance-authority\"\n\n/**\n * Severity colours.\n *\n * Material's baseline scheme has no `success` or `warning` role — it ships\n * primary, secondary, tertiary, and error. Rather than invent two roles that a\n * retheme would then have to know about, each severity borrows the container\n * pair closest to it in meaning. All four pairs are covered by\n * `pnpm check:contrast`, so the content clears AA against the alert's own\n * background in both schemes; a product with real semantic colours should\n * retheme tertiary and primary rather than patch this component.\n *\n * Colour is never the only signal — every severity carries a leading icon for\n * exactly this reason.\n */\nexport const alertVariants = cva(\n  [\n    \"group/alert relative grid w-full gap-x-m3-md gap-y-m3-xs rounded-m3-lg p-m3-lg text-left text-m3-body-md\",\n    \"has-[>[data-slot=alert-icon]]:grid-cols-[auto_1fr]\",\n  ].join(\" \"),\n  {\n    variants: {\n      severity: {\n        info: \"bg-m3-secondary-container text-m3-on-secondary-container\",\n        success: \"bg-m3-primary-container text-m3-on-primary-container\",\n        warning: \"bg-m3-tertiary-container text-m3-on-tertiary-container\",\n        error: \"bg-m3-error-container text-m3-on-error-container\",\n      },\n    },\n    defaultVariants: {\n      severity: \"info\",\n    },\n  }\n)\n\n/**\n * Every part that sits in the text column needs to skip the icon's column when\n * an icon is present. Stated once here rather than repeated on the title, the\n * description, and the actions — three copies of one grid contract is how the\n * three end up disagreeing.\n */\nexport const alertTextColumn =\n  \"group-has-[>[data-slot=alert-icon]]/alert:col-start-2\"\n",
      "type": "registry:ui"
    },
    {
      "path": "src/components/ui/alert.types.ts",
      "content": "/**\n * How serious the message is.\n *\n * Material's baseline scheme has no success or warning colour role, so those\n * two borrow the container pair closest in meaning — see alert-variants.ts.\n */\nexport type AlertSeverity = \"info\" | \"success\" | \"warning\" | \"error\"\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "feedback"
  ],
  "type": "registry:ui"
}