{
  "schemaVersion": 1,
  "target": "react",
  "name": "badge",
  "version": "0.2.0",
  "category": "core",
  "title": "Badge",
  "description": "A compact status label with tinted tones, an optional status dot, and a still live halo for ongoing activity.",
  "tags": [
    "status",
    "label",
    "pill"
  ],
  "requires": {
    "projectTypes": [
      "next",
      "vite-react"
    ],
    "styling": "tailwind-v4"
  },
  "npmDependencies": {},
  "registryDependencies": [],
  "docsPath": "/components/badge",
  "props": [
    {
      "name": "tone",
      "type": "\"neutral\" | \"info\" | \"success\" | \"warning\" | \"danger\"",
      "default": "\"neutral\"",
      "description": "Status hue of the text, border tint, soft background, and dot. Neutral has no fill."
    },
    {
      "name": "dot",
      "type": "boolean",
      "default": "false",
      "description": "Shows a status dot before the label."
    },
    {
      "name": "pulse",
      "type": "boolean",
      "default": "false",
      "description": "Marks ongoing activity with a soft, still halo around the dot (no looping animation). Needs dot. The halo eases in and out when the prop changes."
    },
    {
      "name": "...props",
      "type": "ComponentProps<\"span\">",
      "description": "Any native span attribute."
    }
  ],
  "accessibility": [
    "The label text carries the meaning; the dot and halo are decorative (aria-hidden).",
    "Light-mode text uses the -700 step of each hue so 12px labels meet WCAG AA contrast on the tint.",
    "Nothing loops: the live halo is static, and tone changes are a 200ms color transition that is removed with reduced motion."
  ],
  "states": [
    "neutral",
    "info",
    "success",
    "warning",
    "danger",
    "live"
  ],
  "files": [
    {
      "path": "ui/badge.tsx",
      "content": "import type { ComponentProps } from \"react\";\n\nfunction cx(...classes: (string | false | null | undefined)[]) {\n  return classes.filter(Boolean).join(\" \");\n}\n\n// Text uses the -700 step in light mode so 12px labels pass WCAG AA on the soft tint.\nconst tones = {\n  neutral: \"border-zinc-950/10 text-zinc-700 dark:border-white/15 dark:text-zinc-300\",\n  info: \"border-sky-500/35 bg-sky-500/[0.08] text-sky-700 dark:text-sky-400\",\n  success: \"border-emerald-500/30 bg-emerald-500/[0.08] text-emerald-700 dark:text-emerald-400\",\n  warning: \"border-amber-500/35 bg-amber-500/[0.08] text-amber-700 dark:text-amber-400\",\n  danger: \"border-red-500/35 bg-red-500/[0.08] text-red-700 dark:text-red-400\",\n} as const;\n\nconst dots = {\n  neutral: \"bg-zinc-400 dark:bg-zinc-500\",\n  info: \"bg-sky-500\",\n  success: \"bg-emerald-500\",\n  warning: \"bg-amber-500\",\n  danger: \"bg-red-500\",\n} as const;\n\n// A still halo around the dot: reads as \"live\" without looping motion.\nconst halos = {\n  neutral: \"ring-zinc-400/25 dark:ring-zinc-500/30\",\n  info: \"ring-sky-500/25\",\n  success: \"ring-emerald-500/25\",\n  warning: \"ring-amber-500/25\",\n  danger: \"ring-red-500/25\",\n} as const;\n\nexport type BadgeTone = keyof typeof tones;\n\nexport interface BadgeProps extends ComponentProps<\"span\"> {\n  tone?: BadgeTone;\n  /** Shows a status dot before the label. */\n  dot?: boolean;\n  /**\n   * Marks ongoing activity with a soft halo around the dot. The halo is still (no looping\n   * animation); it eases in and out when the prop changes. Needs `dot`.\n   */\n  pulse?: boolean;\n}\n\nexport function Badge({ tone = \"neutral\", dot = false, pulse = false, className, children, ...props }: BadgeProps) {\n  return (\n    <span\n      data-tone={tone}\n      data-live={(dot && pulse) || undefined}\n      className={cx(\n        \"inline-flex h-5.5 items-center gap-1.5 rounded-md border px-2 text-xs leading-none font-medium whitespace-nowrap\",\n        \"transition-[background-color,border-color,color] duration-200 ease-out motion-reduce:transition-none\",\n        \"[&_svg]:size-3 [&_svg]:shrink-0\",\n        tones[tone],\n        className,\n      )}\n      {...props}\n    >\n      {dot ? (\n        <span\n          aria-hidden=\"true\"\n          className={cx(\n            \"size-1.5 shrink-0 rounded-full\",\n            \"transition-[background-color,box-shadow] duration-200 ease-out motion-reduce:transition-none\",\n            dots[tone],\n            pulse ? cx(\"ring-[3px]\", halos[tone]) : \"ring-0 ring-transparent\",\n          )}\n        />\n      ) : null}\n      {children}\n    </span>\n  );\n}\n"
    }
  ],
  "example": {
    "content": "import { Badge } from \"@/components/seamless/ui/badge\";\n\nexport default function BadgeExample() {\n  return (\n    <div className=\"flex flex-wrap items-center justify-center gap-2\">\n      <Badge>Draft</Badge>\n      <Badge tone=\"info\" dot pulse>\n        Syncing\n      </Badge>\n      <Badge tone=\"success\" dot>\n        Live\n      </Badge>\n      <Badge tone=\"warning\" dot>\n        Needs review\n      </Badge>\n      <Badge tone=\"danger\" dot>\n        Failed\n      </Badge>\n      <Badge className=\"font-mono tabular-nums\">v0.2.0</Badge>\n    </div>\n  );\n}\n"
  }
}
