{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chip",
  "title": "Chip",
  "description": "Chip from the Get IT Done design system.",
  "dependencies": [
    "@radix-ui/react-slot@^1.5.0",
    "class-variance-authority@^0.7.0",
    "lucide-react@^0.379.0"
  ],
  "registryDependencies": [
    "https://zero-design.itdone.de/r/labels.json",
    "https://zero-design.itdone.de/r/labels-context.json",
    "https://zero-design.itdone.de/r/skill-category.json",
    "https://zero-design.itdone.de/r/utils.json",
    "https://zero-design.itdone.de/r/theme.json"
  ],
  "files": [
    {
      "path": "registry/ui/chip.tsx",
      "content": "'use client';\n\nimport { Slot, Slottable } from '@radix-ui/react-slot';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { Check, X } from 'lucide-react';\nimport { cloneElement, forwardRef, isValidElement, type KeyboardEvent, type MouseEvent, type ReactNode } from 'react';\n\nimport { cn } from '@/lib/utils';\nimport { useLabels, usePreset } from '@/components/ui/labels-context';\nimport type { LabelProp } from '@/components/ui/labels';\nimport type { SkillCategory } from '@/components/ui/skill-category';\n\nconst CATEGORY_DOT = {\n  1: 'bg-chart-1',\n  2: 'bg-chart-2',\n  3: 'bg-chart-3',\n  4: 'bg-chart-4',\n  5: 'bg-chart-5',\n  6: 'bg-chart-6',\n  7: 'bg-chart-7',\n  8: 'bg-chart-8',\n} as const;\n\n// Weighted skill chip fills (Figma Chip Style=Weighted): skill category fill with its on-fill text pair. Full class strings so Tailwind finds every token.\n// The on-fill pair is the AA-safe one per category (the Figma file asks for white on all fills, which fails 4.5:1 on several of them).\nconst SKILL_FILL: Record<SkillCategory, string> = {\n  frontend: 'bg-data-skill-frontend-default text-data-skill-frontend-on-fill hover:bg-data-skill-frontend-default active:bg-data-skill-frontend-default',\n  backend: 'bg-data-skill-backend-default text-data-skill-backend-on-fill hover:bg-data-skill-backend-default active:bg-data-skill-backend-default',\n  'devops-cloud': 'bg-data-skill-devops-cloud-default text-data-skill-devops-cloud-on-fill hover:bg-data-skill-devops-cloud-default active:bg-data-skill-devops-cloud-default',\n  'data-ai': 'bg-data-skill-data-ai-default text-data-skill-data-ai-on-fill hover:bg-data-skill-data-ai-default active:bg-data-skill-data-ai-default',\n  'design-ux': 'bg-data-skill-design-ux-default text-data-skill-design-ux-on-fill hover:bg-data-skill-design-ux-default active:bg-data-skill-design-ux-default',\n  mobile: 'bg-data-skill-mobile-default text-data-skill-mobile-on-fill hover:bg-data-skill-mobile-default active:bg-data-skill-mobile-default',\n  security: 'bg-data-skill-security-default text-data-skill-security-on-fill hover:bg-data-skill-security-default active:bg-data-skill-security-default',\n  'testing-qa': 'bg-data-skill-testing-qa-default text-data-skill-testing-qa-on-fill hover:bg-data-skill-testing-qa-default active:bg-data-skill-testing-qa-default',\n  infrastructure: 'bg-data-skill-infrastructure-default text-data-skill-infrastructure-on-fill hover:bg-data-skill-infrastructure-default active:bg-data-skill-infrastructure-default',\n  'project-management': 'bg-data-skill-project-management-default text-data-skill-project-management-on-fill hover:bg-data-skill-project-management-default active:bg-data-skill-project-management-default',\n  'business-strategy': 'bg-data-skill-business-strategy-default text-data-skill-business-strategy-on-fill hover:bg-data-skill-business-strategy-default active:bg-data-skill-business-strategy-default',\n  fallback: 'bg-data-skill-fallback-default text-data-skill-fallback-on-fill hover:bg-data-skill-fallback-default active:bg-data-skill-fallback-default',\n};\n\n// Selected check size per chip size, from the icon size tokens.\nconst CHECK_SIZE = {\n  small: 'h-icon-2xs w-icon-2xs',\n  dense: 'h-icon-2xs w-icon-2xs',\n  compact: 'h-icon-2xs w-icon-2xs',\n  medium: 'h-icon-xs w-icon-xs',\n  control: 'h-icon-xs w-icon-xs',\n  large: 'h-icon-sm w-icon-sm',\n} as const;\n\nconst chipVariants = cva(\n  [\n    'font-regular inline-flex max-w-full items-center whitespace-nowrap rounded-full border border-solid transition-colors',\n    'focus-visible:outline-none focus-visible:ring-focus focus-visible:ring-ring-default focus-visible:ring-offset-focus focus-visible:ring-offset-background',\n    // Chip renders a <div>, not a native form control, so the disabled look is driven by\n    // `aria-disabled`/`data-disabled` rather than the (inapplicable) `disabled:` pseudo-class.\n    'aria-disabled:pointer-events-none aria-disabled:opacity-disabled',\n    'data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-disabled',\n  ],\n  {\n    variants: {\n      variant: {\n        default: [\n          'border-transparent bg-background-neutral-subtle text-text-default',\n          'hover:bg-background-neutral-default',\n          'active:bg-background-neutral-default active:brightness-95',\n        ],\n        // Opt-in stronger neutral fill (slate-200) for chips on white surfaces.\n        neutral: [\n          'border-transparent bg-background-neutral-default text-text-default',\n          'hover:bg-background-neutral-hover',\n          'active:bg-background-neutral-hover active:brightness-95',\n        ],\n        // Regular `border-default` edge (same family as Card/Input); the near-black general-border-default was too heavy.\n        secondary: [\n          'border-border-default bg-transparent text-text-subtle',\n          'hover:bg-background-neutral-subtle',\n          'active:bg-background-neutral-default active:brightness-95',\n        ],\n        // Attio-style outline tag: transparent fill, 1px hairline, `rounded-item` (6px), 12px/500\n        // `text-foreground`. At size `small` the height is a fixed 22px.\n        outline: [\n          'rounded-item border-hairline bg-transparent text-xs font-medium text-foreground',\n          'hover:bg-muted',\n          'active:bg-muted-strong',\n        ],\n        // Muted neutral: quiet surface fill for low-emphasis tags. text-muted is >= 5.71:1 on bg-muted in every theme (text-subtle was 4.34:1 in light). Hover is the neutral hover fill (one step above muted), press is muted-strong; both with text-default.\n        muted: [\n          'border-transparent bg-muted text-text-muted',\n          'hover:bg-background-neutral-hover hover:text-text-default',\n          'active:bg-muted-strong active:text-text-default',\n        ],\n        info: [\n          'border-transparent bg-general-info-default text-general-info-on',\n          'hover:bg-general-info-hover',\n          'active:bg-general-info-active',\n        ],\n        success: [\n          // Rest fill is the `-hover` step: `-default` fails 4.5:1 with `-on` in light themes.\n          'border-transparent bg-general-success-hover text-general-success-on',\n          'hover:brightness-95',\n          'active:brightness-90',\n        ],\n        destructive: [\n          'border-transparent bg-general-error-default text-general-error-on',\n          'hover:bg-general-error-hover',\n          'active:bg-general-error-active',\n        ],\n        // Reserved for a normal, frequent high-priority state: `destructive` stays reserved\n        // for true errors, so high priority uses the warning hue.\n        warning: [\n          // Rest fill is the `-hover` step: `-default` fails 4.5:1 with `-on` in light themes.\n          'border-transparent bg-general-warning-hover text-general-warning-on',\n          'hover:brightness-95',\n          'active:brightness-90',\n        ],\n      },\n      size: {\n        small: 'text-xs px-2 leading-5',\n        // Medium shares the Button sm height (h-control-sm token), so chips and buttons line up in one row.\n        medium: 'h-control-sm text-xs px-2 leading-none',\n        large: 'text-sm px-3 leading-[32px]',\n        // Filter chip: control height (28), 8px padding, item radius, extra-small type role.\n        // Denser than small: 20px high, 4px padding, extra-small type role, for tables and tight rows.\n        dense: 'h-5 gap-1 px-1 typography-text-extra-small leading-none',\n        control: 'h-control-sm gap-gap-control rounded-item px-2 leading-none',\n        // Tag size of the zero chips: 24px high, item radius, detail type role.\n        compact: 'h-6 gap-gap-control rounded-item px-2 typography-detail leading-none',\n      },\n      // Persistent chosen/toggled-on look (e.g. an active filter chip). The color per variant is\n      // resolved via the compoundVariants below, since \"selected\" augments whichever variant is\n      // active rather than replacing it.\n      //\n      // KNOWN TOKEN GAP: none of the general-{info,success,error} color families ship a token\n      // dedicated to \"selected\" — the compoundVariants below fall back to the same `-active` token\n      // used for the transient `:active` (mouse-down) press state, and `default`/`secondary`\n      // selected falls back to the same token used for `:hover`. Per the design-tokens source\n      // (the design tokens), `general-{info,success,error}-active` is in fact\n      // byte-identical to `-default` in both themes today, so there is no existing token that can\n      // give \"selected\" a color identity distinct from hover/press without a token-repo change.\n      // Until that token gap is closed, the leading checkmark rendered below — not this background\n      // color — is the authoritative \"this is selected\" signal; never assume color alone proves it.\n      selected: {\n        true: '',\n      },\n      // Tinted status form (container fill, on-container text) of info, success, warning and destructive.\n      tinted: {\n        true: '',\n      },\n      // Categorical palette (1 to 8, chart tokens): tinted chart-subtle fill with a category coloured border and dot.\n      category: {\n        1: 'border-chart-1',\n        2: 'border-chart-2',\n        3: 'border-chart-3',\n        4: 'border-chart-4',\n        5: 'border-chart-5',\n        6: 'border-chart-6',\n        7: 'border-chart-7',\n        8: 'border-chart-8',\n      },\n      // Text strength of the muted variant. muted (default) uses text-muted, default aligns with the neutral variant (text-default).\n      mutedText: {\n        muted: '',\n        default: '',\n      },\n      // Figma Tag Style=Dashed: transparent chip with a dashed outline (\"add filter\" affordance).\n      dashed: {\n        true: '',\n      },\n      // Figma Tag Hit=True: search/skill match highlight.\n      hit: {\n        true: '',\n      },\n      // Figma Chip Style=Default|Text only|Product dot. `default` leaves the variant look untouched.\n      // Product preset (DesignSystemProvider `preset`). `zero` gives the default chip the outline tag look and the control label the 12/500 detail role.\n      preset: {\n        default: '',\n        zero: '',\n      },\n      chipStyle: {\n        default: '',\n        textOnly: '',\n        productDot: '',\n        weighted: '',\n      },\n    },\n    compoundVariants: [\n      // Control label type: extra-small by default, detail (12/500) in zero. Exclusive classes, so the roles never fight over the font size.\n      { size: 'control', preset: 'default', className: 'typography-text-extra-small' },\n      { size: 'control', preset: 'zero', className: 'typography-detail' },\n      // Zero: the plain chip is the outline tag (item radius, default border, page fill). Placed first so selected and the other compounds can still override the fill.\n      { preset: 'zero', variant: 'default', className: 'rounded-item border-border-default bg-background text-foreground hover:bg-muted active:bg-muted-strong' },\n      { preset: 'zero', variant: ['outline', 'secondary'], className: 'border-border-default' },\n      // Zero: tag heights sit on the control scale (24 and 28) and the label is 12/500. Status meaning (info, success, warning, destructive) is the tinted item-radius badge look, not a solid pill.\n      { preset: 'zero', size: 'small', className: 'h-control-xs leading-none' },\n      { preset: 'zero', size: 'large', className: 'h-control-sm leading-none' },\n      { preset: 'zero', size: ['small', 'medium', 'large'], variant: ['default', 'outline', 'secondary', 'neutral', 'muted'], className: 'font-medium' },\n      { preset: 'zero', variant: ['info', 'success', 'warning', 'destructive'], className: 'rounded-item font-medium' },\n      { variant: 'outline', size: 'small', className: 'h-badge leading-none' },\n      // Filter chip surface: muted fill with a hairline border on the neutral variants.\n      { variant: 'default', size: 'control', className: 'border-hairline bg-muted text-foreground' },\n      { variant: 'outline', size: 'control', className: 'border-hairline bg-muted text-foreground' },\n      { variant: 'default', selected: true, className: 'bg-background-neutral-default' },\n      // Neutral already rests on the default fill, so selected steps up to the hover fill.\n      { variant: 'neutral', selected: true, className: 'bg-background-neutral-hover' },\n      // Muted text at the neutral strength (text-default instead of text-muted). Hover and press keep text-default.\n      { variant: 'muted', mutedText: 'default', className: 'text-text-default' },\n      { variant: 'secondary', selected: true, className: 'bg-background-neutral-default' },\n      { variant: 'outline', selected: true, className: 'bg-muted' },\n      { variant: 'info', selected: true, className: 'bg-general-info-active' },\n      { variant: 'success', selected: true, className: 'bg-general-success-hover' },\n      { variant: 'destructive', selected: true, className: 'bg-general-error-active' },\n      { variant: 'warning', selected: true, className: 'bg-general-warning-hover' },\n      { variant: 'info', tinted: true, className: 'bg-general-info-container text-general-info-on-container hover:bg-general-info-container active:bg-general-info-container active:brightness-95' },\n      { variant: 'success', tinted: true, className: 'bg-general-success-container text-general-success-on-container hover:bg-general-success-container active:bg-general-success-container active:brightness-95' },\n      { variant: 'warning', tinted: true, className: 'bg-general-warning-container text-general-warning-on-container hover:bg-general-warning-container active:bg-general-warning-container active:brightness-95' },\n      { variant: 'destructive', tinted: true, className: 'bg-general-error-container text-general-error-on-container hover:bg-general-error-container active:bg-general-error-container active:brightness-95' },\n      // A selected tinted chip has no stronger fill, so it gets a currentColor border instead.\n      { tinted: true, selected: true, className: 'border-current' },\n      // Categorical chips use the tinted chart fill (AA text contrast in light and dark); border and dot keep the category colour.\n      { category: 1, className: 'bg-chart-subtle-1 text-text-default hover:bg-chart-subtle-1 hover:brightness-95' },\n      { category: 2, className: 'bg-chart-subtle-2 text-text-default hover:bg-chart-subtle-2 hover:brightness-95' },\n      { category: 3, className: 'bg-chart-subtle-3 text-text-default hover:bg-chart-subtle-3 hover:brightness-95' },\n      { category: 4, className: 'bg-chart-subtle-4 text-text-default hover:bg-chart-subtle-4 hover:brightness-95' },\n      { category: 5, className: 'bg-chart-subtle-5 text-text-default hover:bg-chart-subtle-5 hover:brightness-95' },\n      { category: 6, className: 'bg-chart-subtle-6 text-text-default hover:bg-chart-subtle-6 hover:brightness-95' },\n      { category: 7, className: 'bg-chart-subtle-7 text-text-default hover:bg-chart-subtle-7 hover:brightness-95' },\n      { category: 8, className: 'bg-chart-subtle-8 text-text-default hover:bg-chart-subtle-8 hover:brightness-95' },\n      // Text only: no fill, no border, coloured text. The colour follows the variant tone.\n      { chipStyle: 'textOnly', className: 'rounded-item border-transparent bg-transparent text-text-default hover:bg-transparent active:bg-transparent active:brightness-100' },\n      { chipStyle: 'textOnly', variant: 'info', className: 'text-text-brand' },\n      { chipStyle: 'textOnly', variant: 'success', className: 'text-text-success' },\n      { chipStyle: 'textOnly', variant: 'warning', className: 'text-text-warning' },\n      { chipStyle: 'textOnly', variant: 'destructive', className: 'text-text-destructive' },\n      // Product dot: neutral surface and hairline border, square dot in the product colour, dark text.\n      { chipStyle: 'productDot', className: 'gap-2 rounded-item border-hairline bg-background-default text-xs font-medium text-text-default hover:bg-background-default active:bg-background-default active:brightness-100' },\n      // Weighted: filled skill chip, no hairline border, fixed 22px height. Fill and text come from `skill`.\n      { chipStyle: 'weighted', className: 'h-badge border-transparent font-medium leading-none active:brightness-100' },\n      { preset: 'zero', size: 'control', variant: ['default', 'outline'], className: 'border-border-default' },\n      { preset: 'zero', chipStyle: 'productDot', className: 'border-border-default' },\n      { dashed: true, className: 'border-dashed border-input bg-transparent text-text-subtle hover:bg-muted' },\n      // Zero: dashed rests on the page fill with default text, chosen takes the brand tint without a check icon (see `hideCheck`).\n      { dashed: true, preset: 'zero', className: 'bg-background text-text-default' },\n      { selected: true, preset: 'zero', variant: ['default', 'outline', 'secondary', 'neutral'], className: 'border-chip-border-selected bg-chip-background-selected text-chip-foreground-selected hover:bg-chip-background-selected' },\n      { dashed: true, selected: true, preset: 'zero', className: 'border-chip-border-selected bg-chip-background-selected text-chip-foreground-selected hover:bg-chip-background-selected' },\n      { hit: true, className: 'border-transparent bg-highlight-hit-default text-highlight-hit-on hover:bg-highlight-hit-default' },\n    ],\n    defaultVariants: {\n      variant: 'default',\n      size: 'small',\n      preset: 'default',\n    },\n  },\n);\n\nexport interface ChipProps extends React.HTMLAttributes<HTMLDivElement>, Omit<VariantProps<typeof chipVariants>, 'preset'> {\n  /**\n   * Dims the chip and blocks pointer interaction. Chip is not a native form control, so this is\n   * expressed via `aria-disabled`/`data-disabled` rather than the HTML `disabled` attribute — a\n   * nested interactive child (e.g. a trailing remove button) still needs its own `disabled` prop\n   * set independently (see `MultiSelect`'s usage). Also suppresses interactive mode (see `onClick`).\n   */\n  disabled?: boolean;\n  /**\n   * Forces the destructive/error token treatment and sets `aria-invalid`, e.g. for an invalid tag\n   * inside a tag/email input. Takes precedence over `variant`. `aria-invalid` alone is not\n   * reliably announced on a non-widget element, so an additional screen-reader-only \"(invalid)\"\n   * cue is appended to the label.\n   */\n  invalid?: boolean;\n  label?: string;\n  /**\n   * Optional leading icon — e.g. a status glyph so the `info`/`success`/`destructive`/`invalid`\n   * treatments aren't color-only. Ignored while `selected` is true: per Material's filter-chip\n   * anatomy, the leading position always shows the checkmark once selected (mirrors `Button`'s\n   * `dotLeading` taking precedence over `icon`).\n   */\n  icon?: ReactNode;\n  /**\n   * Renders a small leading dot (like `Button`'s `dotLeading`), e.g. a project color. Takes\n   * precedence over `icon` but not over the `selected` checkmark. Purely decorative (`aria-hidden`),\n   * so the label must carry the meaning.\n   */\n  dotLeading?: boolean;\n  /**\n   * Color of the leading dot: any CSS color (`var(--project-blue)`, `#2563eb`) or a token utility\n   * class (`bg-general-info-default`). Classes (strings starting with `bg-`) are applied as\n   * `className`, everything else as inline `background-color`. Defaults to `currentColor`.\n   */\n  dotColor?: string;\n  /** Opt-in trailing close button (icon-xs glyph, 24px hit area); use on static chips, not with `onClick`. */\n  onRemove?: () => void;\n  /** Accessible name of the remove button, a string or a function of the label. Defaults to `Remove <label>`. */\n  removeLabel?: LabelProp<[string | undefined]>;\n  // Screen reader suffixes for a chosen or invalid static chip. Win over the provider text.\n  selectedLabel?: string;\n  invalidLabel?: string;\n  // Visual style (Figma Chip Style). `textOnly` shows only coloured text (tone from `variant`: info, success, warning, destructive, default). `productDot` shows a neutral bordered chip with a square leading dot in `dotColor` (a product or project colour) and dark text.\n  chipStyle?: 'default' | 'textOnly' | 'productDot' | 'weighted';\n  // Skill category of a `weighted` chip: sets the fill and its on-fill text colour. Defaults to `fallback` (Figma Color=Skill Other).\n  skill?: SkillCategory;\n  // Weight text of a `weighted` chip after the label, for example `weight 3` (Figma Weight text, Show weight).\n  weight?: ReactNode;\n  // Text strength of the muted variant. `muted` (default) keeps the quiet text role, `default` aligns the text with the neutral variant.\n  mutedText?: 'muted' | 'default';\n  // Shape of the leading dot. Defaults to `round`, or `square` for a `productDot` chip. An explicit value wins in every chip style.\n  dotShape?: 'round' | 'square';\n  // Muted suffix after the label of a `productDot` chip, for example `.zero` (Figma Chip Style=Product dot, Show suffix). Regular weight, subtle text.\n  suffix?: ReactNode;\n  // Muted secondary text after the label (Figma Tag Meta), for example \"3+ yrs\".\n  meta?: ReactNode;\n  // Small count pill after the label (Figma Tag Action=Count).\n  count?: number | string;\n  // Renders an anchor (link mode). Ignored while `disabled`; the remove button is not rendered in link mode.\n  href?: string;\n  // Renders the single child (for example a router link) with the chip styling and content inside it.\n  asChild?: boolean;\n  // Leading avatar. Unlike `icon` it stays visible when the chip is selected.\n  avatar?: ReactNode;\n  // Keeps `icon` (or `avatar`) in place when selected instead of swapping it for the checkmark.\n  keepLeadingWhenSelected?: boolean;\n}\n\nconst Chip = forwardRef<HTMLDivElement, ChipProps>(\n  (\n    {\n      className,\n      variant,\n      size,\n      selected,\n      disabled,\n      invalid,\n      label,\n      icon,\n      dotLeading = false,\n      dotColor,\n      onRemove,\n      removeLabel,\n      selectedLabel,\n      invalidLabel,\n      meta,\n      suffix,\n      dotShape,\n      count,\n      href,\n      asChild = false,\n      avatar,\n      keepLeadingWhenSelected = false,\n      tinted,\n      mutedText,\n      category,\n      dashed,\n      hit,\n      chipStyle,\n      skill,\n      weight,\n      children,\n      onClick,\n      onKeyDown,\n      ...props\n    },\n    ref,\n  ) => {\n    // Chip is a static tag/label by default (a plain <div>, matching how MultiSelect etc. already\n    // wire a real nested <button> for the parts of a chip that need to be operable). Passing\n    // `onClick` opts a given Chip into the interactive \"filter chip\" pattern instead: a real tab\n    // stop, `aria-pressed` reflecting `selected`, and Enter/Space activation — so the focus-visible\n    // ring above is only ever reachable on a chip that is actually keyboard-operable.\n    const l = useLabels({ remove: removeLabel, selectedSuffix: selectedLabel, invalidSuffix: invalidLabel });\n    const zero = usePreset() === 'zero';\n    const isLink = (href != null || asChild) && !disabled;\n    const interactive = Boolean(onClick) && !disabled && !isLink;\n\n    // WCAG 2.2 SC 2.5.8 (Target Size Minimum): `size=\"small\"` renders a ~22px line box (20px\n    // line-height + 1px border per edge), which clears the 24x24 CSS-px floor for a purely visual\n    // tag/label but not for a real pointer/touch target. Only bump the hit area when the chip is\n    // actually operable (`interactive`) — a static small tag stays visually compact, since it\n    // carries no click/touch target to protect. `min-h-target` = 24px (token utility; the preset replaces the min-height scale, so `min-h-6` compiles to nothing), matching the Tailwind spacing\n    // scale already used for other minimum-dimension cases (e.g. Switch's 24px track height).\n    const effectiveSize = size ?? 'small';\n    const resolvedVariant = invalid ? 'destructive' : variant;\n    // Zero routes status meaning to the tinted look unless the caller sets `tinted` itself.\n    const statusVariant = resolvedVariant === 'info' || resolvedVariant === 'success' || resolvedVariant === 'warning' || resolvedVariant === 'destructive';\n    // Filled status chips (info, success, warning, destructive) carry the on-fill text role; the secondary text inherits it instead of the muted grey.\n    const metaOnFill = statusVariant && chipStyle !== 'textOnly' && chipStyle !== 'productDot' && chipStyle !== 'weighted';\n    // Dense chips that are operable get the same 24px floor.\n    const meetsMinTargetSize = interactive && (effectiveSize === 'small' || effectiveSize === 'dense') ? 'min-h-target' : undefined;\n\n    const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n      onKeyDown?.(event);\n      if (!interactive || event.defaultPrevented) return;\n      if (event.key === 'Enter' || event.key === ' ') {\n        event.preventDefault();\n        onClick?.(event as unknown as MouseEvent<HTMLDivElement>);\n      }\n    };\n\n    // Categorical chips show a dot in the category colour unless a dot colour is given.\n    const categoryDot = category != null && !dotColor ? CATEGORY_DOT[category] : undefined;\n    const effectiveDotColor = dotColor ?? categoryDot;\n    const dotIsClass = effectiveDotColor?.startsWith('bg-');\n    const isProductDot = chipStyle === 'productDot' && !invalid;\n    const isTextOnly = chipStyle === 'textOnly' && !invalid;\n    const isWeighted = chipStyle === 'weighted' && !invalid;\n    const showDot = (dotLeading || isProductDot || isWeighted || categoryDot != null) && !isTextOnly;\n    const dot = showDot ? (\n      <span\n        aria-hidden=\"true\"\n        data-chip-dot=\"\"\n        className={cn(\n          'inline-block h-2 w-2 shrink-0',\n          (dotShape ?? (isProductDot ? 'square' : 'round')) === 'square' ? 'rounded-xs' : 'rounded-full',\n          !isProductDot && 'mr-[6px]',\n          dotIsClass ? effectiveDotColor : 'bg-current',\n        )}\n        style={effectiveDotColor && !dotIsClass ? { backgroundColor: effectiveDotColor } : undefined}\n      />\n    ) : null;\n\n    const keepLeading = keepLeadingWhenSelected || avatar != null;\n    const ownLeading = avatar ?? icon;\n    // Zero: a chosen chip shows the brand tint and no check icon.\n    const hideCheck = zero && !keepLeading;\n    const rawIcon = isTextOnly\n      ? null\n      : selected && hideCheck\n        ? showDot\n          ? null\n          : ownLeading\n      : selected && !(keepLeading && ownLeading)\n        ? <Check aria-hidden=\"true\" data-chip-check=\"\" className={CHECK_SIZE[effectiveSize]} />\n        : showDot\n          ? null\n          : ownLeading;\n    const leadingIcon = rawIcon\n      ? isValidElement<{ className?: string; 'data-chip-check'?: string }>(rawIcon)\n        ? cloneElement(rawIcon, { className: cn('mr-1 shrink-0', rawIcon.props['data-chip-check'] !== undefined ? undefined : avatar != null ? 'h-4 w-4' : 'h-3 w-3', rawIcon.props.className) })\n        : rawIcon\n      : null;\n\n    const Comp = (asChild ? Slot : isLink ? 'a' : 'div') as React.ElementType;\n    const keptSelected = Boolean(selected && keepLeading && ownLeading);\n\n    return (\n      <Comp\n        ref={ref}\n        href={isLink && !asChild ? href : undefined}\n        aria-current={isLink && selected ? 'true' : undefined}\n        role={interactive ? 'button' : undefined}\n        tabIndex={interactive ? 0 : undefined}\n        aria-pressed={interactive && selected != null ? Boolean(selected) : undefined}\n        aria-disabled={disabled || undefined}\n        aria-invalid={invalid || undefined}\n        data-disabled={disabled || undefined}\n        data-invalid={invalid || undefined}\n        data-selected={selected || undefined}\n        onClick={onClick}\n        onKeyDown={handleKeyDown}\n        className={cn(\n          chipVariants({ variant: invalid ? 'destructive' : variant, size, preset: zero ? 'zero' : 'default', selected, tinted: tinted ?? (zero && statusVariant ? true : undefined), mutedText, category: invalid ? undefined : category, dashed, hit, chipStyle: invalid ? 'default' : chipStyle }),\n          isWeighted && SKILL_FILL[skill ?? 'fallback'],\n          meetsMinTargetSize,\n          isLink && 'min-h-target',\n          keptSelected && 'border-general-border-default',\n          className,\n        )}\n        {...props}\n      >\n        {dot}\n        {leadingIcon}\n        {label != null || !asChild ? (\n          <span className=\"-my-1 min-w-0 truncate py-1\" title={label}>\n            {label}\n          </span>\n        ) : null}\n        {/* aria-pressed already announces selection on an interactive chip; a non-interactive\n            \"chosen\" chip has no ARIA widget role to carry that state, so say it in text instead\n            of relying solely on the (currently color-duplicated, see token-gap note above)\n            background tint or the decorative checkmark alone. */}\n        {suffix != null && isProductDot && (\n          <span className=\"-ml-1 shrink-0 font-regular text-text-subtle\" data-chip-suffix=\"\">\n            {suffix}\n          </span>\n        )}\n        {meta != null && <span className={cn('ml-1 shrink-0 font-regular', metaOnFill ? 'text-current' : 'text-text-muted')} data-chip-meta=\"\">\n            {meta}\n          </span>}\n        {count != null && (\n          <span\n            className={cn('ml-1 shrink-0 rounded-full px-1 tabular-nums', tinted ? 'bg-transparent text-current' : 'bg-muted text-text-muted')}\n            data-chip-count=\"\"\n          >\n            {count}\n          </span>\n        )}\n        {isWeighted && weight != null && (\n          <span className=\"ml-1 shrink-0 font-regular typography-text-extra-small\" data-chip-weight=\"\">\n            {weight}\n          </span>\n        )}\n        {selected && !interactive && <span className=\"sr-only\">{l.selectedSuffix}</span>}\n        {invalid && <span className=\"sr-only\">{l.invalidSuffix}</span>}\n        <Slottable>{children}</Slottable>\n        {onRemove && !isLink && (\n          <button\n            type=\"button\"\n            disabled={disabled}\n            aria-label={l.remove(label)}\n            onClick={(event) => {\n              event.stopPropagation();\n              onRemove();\n            }}\n            className={cn(\n              '-my-1 -mr-1 inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-item text-icon-subtle transition-colors',\n              'hover:text-icon-default focus-visible:outline-none focus-visible:ring-focus focus-visible:ring-offset-focus focus-visible:ring-offset-background focus-visible:ring-ring-default',\n              'disabled:pointer-events-none',\n            )}\n          >\n            <X aria-hidden=\"true\" className=\"h-icon-xs w-icon-xs\" />\n          </button>\n        )}\n      </Comp>\n    );\n  },\n);\nChip.displayName = 'Chip';\n\nexport { Chip, chipVariants };\n\n// --- KNOWN TOKEN GAPS (component-library-maintainer cannot close these from Chip.tsx — they are\n// color VALUES owned by the design tokens; Chip only consumes the token by name\n// and must not hardcode a raw hex/palette value around them) ---\n//\n// 1. `secondary` border (CHIP-MAT-01): deliberately moved to the regular `border-default` token (same\n//    family as Card/Input). The edge is intentionally soft; a non-interactive chip needs no 3:1 boundary.\n//\n// 2. `success` and `warning` filled text (WCAG 1.4.3, CHIP-A11Y-04): closed in 0.12.1 without a token\n//    change. `-default` fails 4.5:1 against `-on` in light themes (3.60 and 3.40), so the filled chip\n//    rests on the `-hover` step (5.24 and 4.95 light, 9.29 and 7.89 dark) and presses via brightness.\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}