{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "badge",
  "title": "Badge",
  "type": "registry:ui",
  "description": "Small inline label for status, counts, or tags — sits beside other content, not as a standalone control. Seven variants: `default`, `secondary`, `destructive`, `outline`, `success`, `warning`, `info`.",
  "categories": [
    "data-display"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/components/badge/badge.ts",
      "content": "import { LitElement, css, html, nothing } from 'lit'\nimport { cn } from '../../lib/utils'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\nimport { badgeVariants, type BadgeVariants } from './badge.variants'\n\ntype Variant = NonNullable<BadgeVariants['variant']>\n\n/**\n * <uip-badge> — the registry Badge as a web component.\n *\n * Class strings are React's `badgeVariants` verbatim. Shadow-DOM adaptations:\n *  - `[&>svg]:…` / `[&>span]:min-w-0` can't see slotted children, so they are\n *    repeated as `::slotted(svg)` / `::slotted(span)` on the <slot>;\n *  - `href` renders an <a> (the stand-in for React's `asChild` + <a>), which\n *    is what makes the `[a&]:hover:*` variants apply.\n * Class overrides (React's `className`) target the inner element through\n * `::part(base)`, e.g. `class=\"[&::part(base)]:px-3\"`.\n */\nexport class UipBadge extends LitElement {\n  // :host display can't be a utility class (the host has no template of its own).\n  static styles = [tailwind, css`:host { display: inline-flex; }`]\n\n  static properties = {\n    variant: { reflect: true },\n    wrap: { type: Boolean, reflect: true },\n    href: {},\n    target: {},\n  }\n\n  variant: Variant = 'default'\n  wrap = false\n  href?: string\n  target?: string\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', 'badge')\n  }\n\n  render() {\n    // cn() like React's cn(badgeVariants(…), className): twMerge resolves `wrap`'s\n    // whitespace-normal / rounded-lg / py-1 against the base classes.\n    const classes = cn(badgeVariants({ variant: this.variant, wrap: this.wrap || undefined }))\n    const slot = html`<slot\n      class=\"[&::slotted(svg)]:size-3 [&::slotted(svg)]:shrink-0 [&::slotted(svg)]:pointer-events-none [&::slotted(span)]:min-w-0\"\n    ></slot>`\n    if (this.href) {\n      return html`<a part=\"base\" data-slot=\"badge\" class=${classes} href=${this.href} target=${this.target ?? nothing}\n        >${slot}</a\n      >`\n    }\n    return html`<span part=\"base\" data-slot=\"badge\" class=${classes}>${slot}</span>`\n  }\n}\n\ncustomElements.get('uip-badge') || customElements.define('uip-badge', UipBadge)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-badge': UipBadge\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/badge/badge.ts"
    },
    {
      "path": "packages/elements-lit/src/components/badge/badge.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 consuming Vue SFCs can import without creating a circular\n * dependency through the index. See card.variants.ts for the canonical\n * example + the SSR symptom that motivated the split.\n */\n\nexport const badgeVariants = cva(\n  // text-overflow/ellipsis cannot apply to a flex container's anonymous text,\n  // so truncation is opt-in via a child span (`<Badge><span class=\"truncate\">\n  // …</span></Badge>`); min-w-0 lets that span shrink inside the flex root.\n  'inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit max-w-full whitespace-nowrap shrink-0 [&>svg]:size-3 [&>svg]:shrink-0 [&>span]:min-w-0 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-colors duration-200 overflow-hidden',\n  {\n    variants: {\n      variant: {\n        default: 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90',\n        secondary: 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',\n        destructive:\n          'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',\n        outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',\n        success: 'border-transparent bg-success/10 text-success dark:text-success [a&]:hover:bg-success/20',\n        warning: 'border-transparent bg-warning/10 text-warning dark:text-warning [a&]:hover:bg-warning/20',\n        info: 'border-transparent bg-info/10 text-info dark:text-info [a&]:hover:bg-info/20',\n      },\n      // Allow multi-line labels. rounded-lg + extra leading keeps a wrapped\n      // badge readable instead of a cramped two-line stadium pill.\n      wrap: {\n        true: 'whitespace-normal rounded-lg py-1 leading-snug',\n      },\n    },\n    defaultVariants: {\n      variant: 'default',\n    },\n  },\n)\n\nexport type BadgeVariants = VariantProps<typeof badgeVariants>\n",
      "type": "registry:file",
      "target": "src/uip/components/badge/badge.variants.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority",
    "lit"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
