{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "badge",
  "type": "registry:ui",
  "title": "Badge",
  "description": "Small visual marker — status pill / counter / label. Six variants; renders as span (default) or anchor (with href).",
  "files": [
    {
      "path": "registry/ui/badge.tsx",
      "type": "registry:ui",
      "target": "components/ui/badge.tsx",
      "content": "/** @jsxImportSource hono/jsx */\nimport type { PropsWithChildren } from \"hono/jsx\"\nimport { cloneElement, isValidElement } from \"hono/jsx\"\nimport { cn, type ClassValue } from \"@/registry/lib/cn\"\n\n// Badge — shadcn-htmx, htmx v4 + Tailwind v4.\n//\n// Source of truth (variants + base class 1:1):\n//   repos/shadcn-ui/apps/v4/registry/new-york-v4/ui/badge.tsx\n//\n// A non-interactive visual marker. Renders as <span> by default. Pass\n// `as=\"a\"` (or `asChild` with a child anchor) to make it a link — the\n// upstream uses Radix Slot.Root; we use cloneElement.\n//\n// Accessibility:\n//   - The badge's content (text) IS the accessible name. If you render an\n//     icon-only badge, set `ariaLabel` so screen readers can name it.\n//   - Status-style badges (\"New\", \"3 unread\") that update in place should\n//     live inside an aria-live region (use Alert / Toast for that, not\n//     Badge). Badge itself is presentational.\n//   - See repos/mdn/files/en-us/web/html/reference/elements/span/index.md for\n//     <span> semantics (none — it's a generic inline container).\n\nexport type BadgeVariant =\n  | \"default\"\n  | \"secondary\"\n  | \"destructive\"\n  | \"outline\"\n  | \"ghost\"\n  | \"link\"\n\nconst base =\n  \"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] \" +\n  \"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 \" +\n  \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 \" +\n  \"[&>svg]:pointer-events-none [&>svg]:size-3\"\n\nconst variants: Record<BadgeVariant, string> = {\n  default: \"bg-primary text-primary-foreground [a&]:hover:bg-primary/90\",\n  secondary: \"bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90\",\n  destructive:\n    \"bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90\",\n  outline:\n    \"border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground\",\n  ghost: \"[a&]:hover:bg-accent [a&]:hover:text-accent-foreground\",\n  link: \"text-primary underline-offset-4 [a&]:hover:underline\",\n}\n\nexport function badgeClasses(opts?: {\n  variant?: BadgeVariant\n  class?: ClassValue\n}): string {\n  const variant = opts?.variant ?? \"default\"\n  return cn(base, variants[variant], opts?.class)\n}\n\ntype BadgeProps = PropsWithChildren<{\n  variant?: BadgeVariant\n  class?: ClassValue\n  id?: string\n  ariaLabel?: string\n  ariaLabelledby?: string\n  // Render as a different element. <a> is the common case (link badge).\n  as?: \"span\" | \"a\" | \"div\" | \"button\"\n  href?: string\n  // SSR-friendly equivalent of shadcn's asChild — clone the single JSX\n  // child and merge classes onto it. Useful for wrapping a custom <Link>.\n  asChild?: boolean\n}>\n\nexport function Badge(props: BadgeProps) {\n  const {\n    children,\n    variant,\n    class: className,\n    as,\n    href,\n    asChild,\n    ariaLabel,\n    ariaLabelledby,\n    id,\n    ...rest\n  } = props\n\n  const classes = badgeClasses({ variant, class: className })\n\n  if (asChild && isValidElement(children)) {\n    const child = children as any\n    return cloneElement(child, {\n      ...rest,\n      class: cn(classes, child?.props?.class),\n      \"data-slot\": \"badge\",\n      \"data-variant\": variant ?? \"default\",\n      \"aria-label\": ariaLabel,\n      \"aria-labelledby\": ariaLabelledby,\n    })\n  }\n\n  const Tag: any = as ?? (href ? \"a\" : \"span\")\n  return (\n    <Tag\n      id={id}\n      href={href}\n      data-slot=\"badge\"\n      data-variant={variant ?? \"default\"}\n      class={classes}\n      aria-label={ariaLabel}\n      aria-labelledby={ariaLabelledby}\n      {...rest}\n    >\n      {children}\n    </Tag>\n  )\n}\n"
    },
    {
      "path": "registry/jinja2/badge.html",
      "type": "registry:file",
      "target": "templates/components/badge.html",
      "content": "{# Badge macro — shadcn-htmx, htmx v4 + Tailwind v4.\n   Mirrors registry/ui/badge.tsx. Renders <span> by default; pass tag=\"a\"\n   + href for a link badge, tag=\"button\" for an interactive one.\n\n   Usage:\n     {% from \"components/badge.html\" import badge %}\n     {{ badge(\"New\", variant=\"default\") }}\n     {{ badge(\"Unstable\", variant=\"destructive\") }}\n     {{ badge(\"Docs\", tag=\"a\", href=\"/docs\") }} #}\n\n{% macro badge(\n    text,\n    variant=\"default\",\n    tag=\"span\",\n    href=none,\n    id=none,\n    aria_label=none,\n    aria_labelledby=none,\n    extra_class=\"\",\n    **attrs\n) %}\n{%- set base -%}\ninline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3\n{%- endset -%}\n{%- set variants = {\n    \"default\": \"bg-primary text-primary-foreground [a&]:hover:bg-primary/90\",\n    \"secondary\": \"bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90\",\n    \"destructive\": \"bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90\",\n    \"outline\": \"border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground\",\n    \"ghost\": \"[a&]:hover:bg-accent [a&]:hover:text-accent-foreground\",\n    \"link\": \"text-primary underline-offset-4 [a&]:hover:underline\"\n} -%}\n<{{ tag }}\n  {%- if id %} id=\"{{ id }}\"{% endif %}\n  {%- if href and tag == \"a\" %} href=\"{{ href }}\"{% endif %}\n  {%- if aria_label %} aria-label=\"{{ aria_label }}\"{% endif %}\n  {%- if aria_labelledby %} aria-labelledby=\"{{ aria_labelledby }}\"{% endif %}\n  data-slot=\"badge\"\n  data-variant=\"{{ variant }}\"\n  class=\"{{ base }} {{ variants[variant] }} {{ extra_class }}\"\n  {%- for k, v in attrs.items() %} {{ k|replace('_', '-') }}=\"{{ v }}\"{% endfor -%}\n>{{ text }}</{{ tag }}>\n{% endmacro %}\n"
    },
    {
      "path": "registry/go-templates/badge.tmpl",
      "type": "registry:file",
      "target": "components/badge.tmpl",
      "content": "{{/*\n  Badge template — shadcn-htmx, htmx v4 + Tailwind v4.\n  Mirrors registry/ui/badge.tsx.\n\n  Usage:\n\n      type BadgeArgs struct {\n          Text                 string\n          Variant              string // default | secondary | destructive | outline | ghost | link\n          Tag                  string // \"span\" (default) | \"a\" | \"div\" | \"button\"\n          Href                 string\n          ID, AriaLabel        string\n          AriaLabelledby       string\n          Attrs                map[string]string\n      }\n*/}}\n\n{{define \"badge\"}}\n{{- $tag := or .Tag \"span\" -}}\n{{- $variant := or .Variant \"default\" -}}\n{{- $base := \"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3\" -}}\n{{- $variants := dict\n    \"default\" \"bg-primary text-primary-foreground [a&]:hover:bg-primary/90\"\n    \"secondary\" \"bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90\"\n    \"destructive\" \"bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90\"\n    \"outline\" \"border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground\"\n    \"ghost\" \"[a&]:hover:bg-accent [a&]:hover:text-accent-foreground\"\n    \"link\" \"text-primary underline-offset-4 [a&]:hover:underline\" -}}\n<{{$tag}}\n  {{- if .ID}} id=\"{{.ID}}\"{{end}}\n  {{- if and (eq $tag \"a\") .Href}} href=\"{{.Href}}\"{{end}}\n  {{- if .AriaLabel}} aria-label=\"{{.AriaLabel}}\"{{end}}\n  {{- if .AriaLabelledby}} aria-labelledby=\"{{.AriaLabelledby}}\"{{end}}\n  data-slot=\"badge\" data-variant=\"{{$variant}}\"\n  class=\"{{$base}} {{index $variants $variant}}\"\n  {{- range $k, $v := .Attrs}} {{$k}}=\"{{$v}}\"{{end -}}\n>{{.Text}}</{{$tag}}>\n{{end}}\n"
    },
    {
      "path": "registry/phoenix/badge.ex",
      "type": "registry:file",
      "target": "lib/my_app_web/components/badge.ex",
      "content": "defmodule ShadcnHtmx.Components.Badge do\n  @moduledoc \"\"\"\n  Badge — shadcn-htmx, htmx v4 + Tailwind v4 for Phoenix.\n\n  Mirrors registry/ui/badge.tsx. Non-interactive visual marker; renders\n  as `<span>` by default. Pass `as=\"a\"` + `href` for a link badge,\n  `as=\"button\"` for a clickable one.\n\n  Accessibility: badge text is the accessible name. Use `aria-label` for\n  icon-only badges. Status badges that change in place should live inside\n  an `aria-live` region (see Alert / Toast).\n  \"\"\"\n\n  use Phoenix.Component\n\n  @base \"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] \" <>\n          \"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 \" <>\n          \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 \" <>\n          \"[&>svg]:pointer-events-none [&>svg]:size-3\"\n\n  @variants %{\n    \"default\" => \"bg-primary text-primary-foreground [a&]:hover:bg-primary/90\",\n    \"secondary\" =>\n      \"bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90\",\n    \"destructive\" =>\n      \"bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90\",\n    \"outline\" =>\n      \"border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground\",\n    \"ghost\" => \"[a&]:hover:bg-accent [a&]:hover:text-accent-foreground\",\n    \"link\" => \"text-primary underline-offset-4 [a&]:hover:underline\"\n  }\n\n  attr :variant, :string,\n    default: \"default\",\n    values: ~w(default secondary destructive outline ghost link)\n\n  attr :as, :string, default: \"span\", values: ~w(span a div button)\n  attr :href, :string, default: nil\n  attr :class, :string, default: nil\n  attr :rest, :global\n\n  slot :inner_block, required: true\n\n  def badge(assigns) do\n    assigns =\n      assigns\n      |> assign(:variant_class, Map.fetch!(@variants, assigns.variant))\n      |> assign(:base_class, @base)\n\n    ~H\"\"\"\n    <.dynamic_tag\n      tag_name={@as}\n      data-slot=\"badge\"\n      data-variant={@variant}\n      href={if @as == \"a\", do: @href}\n      class={[@base_class, @variant_class, @class]}\n      {@rest}\n    >\n      {render_slot(@inner_block)}\n    </.dynamic_tag>\n    \"\"\"\n  end\nend\n"
    },
    {
      "path": "registry/html/badge.html",
      "type": "registry:file",
      "target": "snippets/badge.html",
      "content": "<!--\n  shadcn-htmx — raw HTML badge snippets.\n\n  BASE (all variants):\n    inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden\n    rounded-full border border-transparent px-2 py-0.5 text-xs font-medium\n    whitespace-nowrap transition-[color,box-shadow]\n    focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50\n    aria-invalid:border-destructive aria-invalid:ring-destructive/20\n    dark:aria-invalid:ring-destructive/40\n    [&>svg]:pointer-events-none [&>svg]:size-3\n-->\n\n<!-- default -->\n<span data-slot=\"badge\" data-variant=\"default\"\n  class=\"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3 bg-primary text-primary-foreground\">\n  New\n</span>\n\n<!-- secondary -->\n<span data-slot=\"badge\" data-variant=\"secondary\"\n  class=\"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3 bg-secondary text-secondary-foreground\">\n  Beta\n</span>\n\n<!-- destructive -->\n<span data-slot=\"badge\" data-variant=\"destructive\"\n  class=\"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3 bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40\">\n  Unstable\n</span>\n\n<!-- outline -->\n<span data-slot=\"badge\" data-variant=\"outline\"\n  class=\"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3 border-border text-foreground\">\n  Draft\n</span>\n\n<!-- ghost -->\n<span data-slot=\"badge\" data-variant=\"ghost\"\n  class=\"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3 text-foreground\">\n  Pinned\n</span>\n\n<!-- Link variant (as anchor) -->\n<a href=\"/docs/badge\" data-slot=\"badge\" data-variant=\"link\"\n  class=\"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3 text-primary underline-offset-4 hover:underline\">\n  Read docs\n</a>\n\n<!-- Icon-only badge — set aria-label so it has an accessible name -->\n<span data-slot=\"badge\" data-variant=\"destructive\" aria-label=\"3 unread notifications\"\n  class=\"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3 bg-destructive text-white\">\n  3\n</span>\n"
    }
  ]
}
