{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "badge",
  "title": "Badge",
  "description": "A small label for tier, category, new and demo data: neutral, free, pro, new and demo variants that read in light and dark.",
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/manniche/badge/badge.tsx",
      "content": "// A small label that says what kind of thing sits next to it: its tier, its category, that it is new, or that the\n// figures around it are example data. One family: same height, same corner, one quiet look per meaning. It is never\n// a button and never takes focus; when the label matters to a link, point the link's aria-describedby at its id.\nimport type { ComponentPropsWithoutRef, ReactNode } from 'react'\nimport { cn } from '@/lib/utils'\n\nexport type BadgeVariant = 'neutral' | 'free' | 'pro' | 'new' | 'demo'\n\nexport type BadgeProps = Omit<ComponentPropsWithoutRef<'span'>, 'children'> & {\n  /**\n   * What the label means, which sets how loud it is:\n   * - `neutral`: a category or any plain tag (\"controls\", \"surfaces\"), set in mono on a faint fill. The default.\n   * - `free`: free or open source (\"Free\", \"MIT\"), a hairline outline that steps back.\n   * - `pro`: the paid tier (\"Pro\"), inverted to the page's text colour, so it is the one label that stands out.\n   * - `new`: recently added (\"New\"), a raised key with a small success dot. The word carries the meaning, not the dot.\n   * - `demo`: marks example data (\"Demo data\"), a dashed outline, so made-up figures never pass for real ones.\n   */\n  variant?: BadgeVariant\n  /** The visible text. Keep it to a word or two. */\n  children: ReactNode\n  /** Classes for the badge span. */\n  className?: string\n}\n\nconst VARIANT: Record<BadgeVariant, string> = {\n  neutral: 'bg-foreground/[0.06] px-[7px] font-mono text-[11.5px] text-muted-foreground',\n  free: 'px-2 text-muted-foreground shadow-[inset_0_0_0_1px_color-mix(in_oklab,var(--foreground)_16%,transparent)]',\n  pro: 'bg-foreground px-2 text-background shadow-[inset_0_1px_0_color-mix(in_oklab,var(--background)_35%,transparent),inset_0_-1.5px_0_rgb(0_0_0/0.2)]',\n  new: 'gap-1.5 bg-muted px-2 text-foreground shadow-[inset_0_1px_0_color-mix(in_oklab,var(--foreground)_7%,transparent),inset_0_-1.5px_0_rgb(0_0_0/0.14),0_0_0_1px_color-mix(in_oklab,var(--foreground)_8%,transparent)] dark:shadow-[inset_0_1px_0_color-mix(in_oklab,var(--foreground)_7%,transparent),inset_0_-1.5px_0_rgb(0_0_0/0.45),0_0_0_1px_color-mix(in_oklab,var(--foreground)_8%,transparent)]',\n  demo: 'border border-dashed border-foreground/25 px-[7px] text-muted-foreground',\n}\n\n/** A small, calm label for a tier, a category, something new or example data. Not interactive. */\nexport function Badge({ variant = 'neutral', children, className, ...rest }: BadgeProps) {\n  return (\n    <span\n      data-variant={variant}\n      className={cn(\n        'inline-flex h-[22px] flex-none items-center rounded-[6px] text-xs leading-none font-medium whitespace-nowrap',\n        VARIANT[variant],\n        className,\n      )}\n      {...rest}\n    >\n      {variant === 'new' && <span aria-hidden className=\"size-1.5 flex-none rounded-full bg-success\" />}\n      {children}\n    </span>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "meta": {
    "tier": "free",
    "added": "2026-10-07",
    "usedIn": [
      "command-search",
      "gallery-card",
      "locked-code"
    ],
    "props": [
      {
        "name": "variant",
        "type": "BadgeVariant",
        "default": "'neutral'",
        "required": false,
        "description": "What the label means, which sets how loud it is: - `neutral`: a category or any plain tag (\"controls\", \"surfaces\"), set in mono on a faint fill. The default. - `free`: free or open source (\"Free\", \"MIT\"), a hairline outline that steps back. - `pro`: the paid tier (\"Pro\"), inverted to the page's text colour, so it is the one label that stands out. - `new`: recently added (\"New\"), a raised key with a small success dot. The word carries the meaning, not the dot. - `demo`: marks example data (\"Demo data\"), a dashed outline, so made-up figures never pass for real ones."
      },
      {
        "name": "children",
        "type": "ReactNode",
        "default": null,
        "required": true,
        "description": "The visible text. Keep it to a word or two."
      },
      {
        "name": "className",
        "type": "string",
        "default": null,
        "required": false,
        "description": "Classes for the badge span."
      }
    ],
    "extends": "Omit<ComponentPropsWithoutRef<'span'>, 'children'>",
    "a11y": [
      {
        "topic": "screen-reader",
        "text": "Plain text in a span, not interactive. Pass an id and point a link's aria-describedby at it, so the tier is read with the link."
      }
    ]
  },
  "categories": [
    "controls"
  ],
  "type": "registry:component"
}