{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "alert",
  "title": "Alert",
  "type": "registry:ui",
  "description": "Static, in-flow notice block with a leading icon, title, and description. Use for inline page-level messages — info banners, success confirmations, warning callouts. Two tones: `default` and `destructive`.",
  "categories": [
    "feedback"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/components/alert/alert.ts",
      "content": "import { LitElement, css, html, nothing } from 'lit'\nimport { html as staticHtml, unsafeStatic } from 'lit/static-html.js'\nimport { AlertCircle, CheckCircle, Info, TriangleAlert } from 'lucide'\nimport { cn } from '../../lib/utils'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\nimport { icon } from '../../lib/icon'\nimport { alertVariants, type AlertVariants } from './alert.variants'\n\ntype Variant = NonNullable<AlertVariants['variant']>\nexport type AlertIcon = 'info' | 'warning' | 'error' | 'success'\n\nconst builtInIcons = {\n  error: { node: AlertCircle, name: 'circle-alert' },\n  success: { node: CheckCircle, name: 'circle-check-big' },\n  warning: { node: TriangleAlert, name: 'triangle-alert' },\n  info: { node: Info, name: 'info' },\n} as const\n\n/**\n * <uip-alert> — the registry Alert as a web component, with <uip-alert-title>\n * and <uip-alert-description> for the composition API.\n *\n *   <uip-alert variant=\"destructive\">\n *     <svg><!-- slotted icon, like React's <AlertCircle /> child --></svg>\n *     <uip-alert-title>Error</uip-alert-title>\n *     <uip-alert-description>Something went wrong.</uip-alert-description>\n *   </uip-alert>\n *\n *   <uip-alert icon=\"info\" title=\"Heads up\" text=\"…\"></uip-alert>\n *\n * Class strings are React's verbatim. Two shadow-DOM adaptations (a slotted\n * <svg> is not a child of the inner root, so `[&>svg]` can't reach it):\n *  - the slot carries the icon rules as `[&::slotted(svg)]:…`;\n *  - React's `[&>svg~*]:pl-7` (pad everything after the icon) becomes\n *    `data-icon` on the inner root, mirrored onto slotted\n *    <uip-alert-title>/<uip-alert-description> so they pad themselves; the\n *    `[&>svg+div]:translate-y-[-3px]` nudge becomes `data-nudge` on the\n *    description when no title precedes it.\n *\n * `icon` renders a built-in Lucide glyph (info | warning | error | success);\n * slot your own <svg> instead for custom icons. `title` / `text` are the\n * shorthand for a title + description; combine them with slotted children\n * freely (props render first).\n *\n * Parts: `base` on each element (style React's `className` overrides with\n * `class=\"[&::part(base)]:…\"` on the host).\n */\nexport class UipAlert extends LitElement {\n  // React's root is a block <div>.\n  static styles = [tailwind, css`:host { display: block; }`]\n\n  static properties = {\n    variant: { reflect: true },\n    icon: { reflect: true },\n    title: {},\n    text: {},\n    hasSlottedIcon: { state: true },\n    hasSlottedTitle: { state: true },\n  }\n\n  variant: Variant = 'default'\n  icon?: AlertIcon\n  override title: string = ''\n  text?: string\n  private hasSlottedIcon = false\n  private hasSlottedTitle = false\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'alert')\n  }\n\n  private get hasIcon() {\n    return !!this.icon || this.hasSlottedIcon\n  }\n\n  private onSlotChange(e: Event) {\n    const assigned = (e.target as HTMLSlotElement).assignedElements()\n    this.hasSlottedIcon = assigned.some((el) => el.localName === 'svg')\n    this.hasSlottedTitle = assigned.some(\n      (el) => el.localName === 'uip-alert-title' || el.getAttribute('data-slot') === 'alert-title',\n    )\n    // Mirror the icon state onto the slotted parts (see class comment).\n    const hasTitle = !!this.title || this.hasSlottedTitle\n    for (const el of assigned) {\n      if (el.localName !== 'uip-alert-title' && el.localName !== 'uip-alert-description') continue\n      if (this.hasIcon) el.setAttribute('data-icon', '')\n      else el.removeAttribute('data-icon')\n      if (el.localName === 'uip-alert-description') {\n        if (this.hasIcon && !hasTitle) el.setAttribute('data-nudge', '')\n        else el.removeAttribute('data-nudge')\n      }\n    }\n  }\n\n  render() {\n    const hasIcon = this.hasIcon\n    const builtIn = this.icon ? builtInIcons[this.icon] : undefined\n    // A slotted <svg> also matches the variants' `[&>svg]` tint when it is a\n    // real shadow child — it isn't, so repeat the colour on the slot. A slotted\n    // svg's own page classes (e.g. text-info) still apply from the light DOM.\n    const slottedIconColor = this.variant === 'destructive' ? '[&::slotted(svg)]:text-destructive' : '[&::slotted(svg)]:text-foreground'\n    return html`<div part=\"base\" role=\"alert\" data-uipkge=\"\" data-slot=\"alert\" ?data-icon=${hasIcon} class=${alertVariants({ variant: this.variant })}>\n      ${builtIn ? icon(builtIn.node, builtIn.name) : nothing}\n      ${this.title\n        ? html`<p data-uipkge=\"\" data-slot=\"alert-title\" class=${cn('mb-1 text-sm leading-none font-medium tracking-tight', hasIcon && 'pl-7')}>\n            ${this.title}\n          </p>`\n        : nothing}\n      ${this.text\n        ? html`<div\n            data-uipkge=\"\"\n            data-slot=\"alert-description\"\n            class=${cn(\n              'text-muted-foreground text-sm leading-relaxed [&_p]:leading-relaxed',\n              hasIcon && 'pl-7',\n              hasIcon && !this.title && !this.hasSlottedTitle && 'translate-y-[-3px]',\n            )}\n          >\n            ${this.text}\n          </div>`\n        : nothing}\n      <slot\n        class=${cn(\n          '[&::slotted(svg)]:absolute [&::slotted(svg)]:left-4 [&::slotted(svg)]:top-4 [&::slotted(svg)]:size-4',\n          slottedIconColor,\n        )}\n        @slotchange=${this.onSlotChange}\n      ></slot>\n    </div>`\n  }\n}\n\nconst titleClasses = 'mb-1 text-sm leading-none font-medium tracking-tight'\nconst descriptionClasses = 'text-muted-foreground text-sm leading-relaxed [&_p]:leading-relaxed'\n\n/** <uip-alert-title> — React's AlertTitle (`as` = h1-h6 | div, default h5). */\nexport class UipAlertTitle extends LitElement {\n  static styles = [tailwind, css`:host { display: block; }`]\n\n  static properties = {\n    as: { reflect: true },\n    dataIcon: { type: Boolean, attribute: 'data-icon', reflect: true },\n  }\n\n  as: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'div' = 'h5'\n  /** Set by <uip-alert> when an icon precedes the title (React's `[&>svg~*]:pl-7`). */\n  dataIcon = false\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'alert-title')\n  }\n\n  render() {\n    const tag = unsafeStatic(this.as)\n    return staticHtml`<${tag} part=\"base\" data-uipkge=\"\" data-slot=\"alert-title\" class=${cn(titleClasses, this.dataIcon && 'pl-7')}><slot></slot></${tag}>`\n  }\n}\n\n/** <uip-alert-description> — React's AlertDescription. */\nexport class UipAlertDescription extends LitElement {\n  static styles = [tailwind, css`:host { display: block; }`]\n\n  static properties = {\n    dataIcon: { type: Boolean, attribute: 'data-icon', reflect: true },\n    dataNudge: { type: Boolean, attribute: 'data-nudge', reflect: true },\n  }\n\n  /** Set by <uip-alert> when an icon precedes the description (React's `[&>svg~*]:pl-7`). */\n  dataIcon = false\n  /** Set by <uip-alert> when the icon is directly adjacent (React's `[&>svg+div]:translate-y-[-3px]`). */\n  dataNudge = false\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'alert-description')\n  }\n\n  render() {\n    return html`<div\n      part=\"base\"\n      data-uipkge=\"\"\n      data-slot=\"alert-description\"\n      class=${cn(descriptionClasses, this.dataIcon && 'pl-7', this.dataNudge && 'translate-y-[-3px]')}\n    >\n      <slot></slot>\n    </div>`\n  }\n}\n\ncustomElements.get('uip-alert') || customElements.define('uip-alert', UipAlert)\ncustomElements.get('uip-alert-title') || customElements.define('uip-alert-title', UipAlertTitle)\ncustomElements.get('uip-alert-description') || customElements.define('uip-alert-description', UipAlertDescription)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-alert': UipAlert\n    'uip-alert-title': UipAlertTitle\n    'uip-alert-description': UipAlertDescription\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/alert/alert.ts"
    },
    {
      "path": "packages/elements-lit/src/components/alert/alert.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\n/**\n * Variant definitions live in their own file (rather than the package\n * `index.ts`) so `Alert.vue` can `import { alertVariants } from\n * './alert.variants'` without creating a circular dependency through the\n * index. See card.variants.ts for the same pattern + the SSR symptom that\n * motivated the split.\n *\n * Layout: composition children (svg + AlertTitle + AlertDescription) are\n * direct descendants of the root so `[&>svg]` absolute positioning works.\n */\nexport const alertVariants = cva(\n  'relative w-full rounded-lg border p-4 text-sm [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:size-4 [&>svg]:text-foreground [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px]',\n  {\n    variants: {\n      variant: {\n        default: 'bg-background text-foreground border-border',\n        destructive: 'border-destructive/20 text-destructive bg-destructive/5 [&>svg]:text-destructive',\n      },\n    },\n    defaultVariants: {\n      variant: 'default',\n    },\n  },\n)\n\nexport type AlertVariants = VariantProps<typeof alertVariants>\n",
      "type": "registry:file",
      "target": "src/uip/components/alert/alert.variants.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority",
    "lit",
    "lucide"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
