{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "chip",
  "title": "Chip",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-svelte/components/chip/Chip.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n  import type { ChipVariants } from './chip.variants'\n\n  export interface ChipProps extends HTMLAttributes<HTMLSpanElement> {\n    variant?: ChipVariants['variant']\n    size?: ChipVariants['size']\n    /** Allow the label to wrap onto multiple lines instead of clipping. */\n    wrap?: boolean\n    /** Show a dismiss button; `onclose` fires after the leave animation. */\n    closable?: boolean\n    children?: Snippet\n    /** Fires when the chip requests removal (after the leave animation). */\n    onclose?: () => void\n  }\n</script>\n\n<script lang=\"ts\">\n  import { X } from '@lucide/svelte'\n  import { cn } from '$lib/utils'\n  import { chipVariants } from './chip.variants'\n\n  let {\n    class: className,\n    variant = 'default',\n    size = 'default',\n    wrap,\n    closable = false,\n    children,\n    onclose,\n    ...restProps\n  }: ChipProps = $props()\n\n  let leaving = $state(false)\n\n  function onClose() {\n    if (leaving) return\n    leaving = true\n    const reduce = typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches\n    window.setTimeout(() => {\n      onclose?.()\n    }, reduce ? 0 : 160)\n  }\n</script>\n\n<span\n  data-uipkge\n  data-slot=\"chip\"\n  data-variant={variant}\n  data-leaving={leaving || undefined}\n  class={cn(chipVariants({ variant, size, wrap }), 'chip-enter', leaving && 'chip-leave', className)}\n  {...restProps}\n>\n  {@render children?.()}\n  {#if closable}\n    <button\n      type=\"button\"\n      aria-label=\"Remove item\"\n      class=\"focus-visible:ring-ring hover:bg-foreground/10 ml-1 inline-flex min-h-6 min-w-6 items-center justify-center rounded-full transition-transform duration-150 focus-visible:ring-1 focus-visible:outline-none active:scale-90\"\n      onclick={(e) => {\n        e.stopPropagation()\n        onClose()\n      }}\n    >\n      <X class=\"size-3\" aria-hidden=\"true\" />\n    </button>\n  {/if}\n</span>\n\n<style>\n  @keyframes chip-enter {\n    from {\n      opacity: 0;\n      transform: scale(0.88);\n    }\n    to {\n      opacity: 1;\n      transform: scale(1);\n    }\n  }\n\n  @keyframes chip-leave {\n    to {\n      opacity: 0;\n      transform: scale(0.88);\n    }\n  }\n\n  :global([data-slot='chip']).chip-enter {\n    animation: chip-enter 180ms cubic-bezier(0.22, 1.2, 0.36, 1) both;\n  }\n\n  :global([data-slot='chip']).chip-leave {\n    animation: chip-leave 160ms ease-in both;\n    pointer-events: none;\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    :global([data-slot='chip']).chip-enter,\n    :global([data-slot='chip']).chip-leave {\n      animation: none !important;\n    }\n  }\n</style>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/chip/Chip.svelte"
    },
    {
      "path": "packages/registry-svelte/components/chip/ChipGroup.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface ChipGroupSlotArgs {\n    selected: string[]\n    multiple: boolean\n    filter: boolean\n    isSelected: (value: string) => boolean\n    toggle: (value: string) => void\n  }\n\n  /** React parity alias — same shape as `ChipGroupSlotArgs`. */\n  export type ChipGroupRenderProps = ChipGroupSlotArgs\n\n  export interface ChipGroupProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {\n    selected?: string[]\n    multiple?: boolean\n    filter?: boolean\n    column?: boolean\n    mandatory?: boolean\n    max?: number\n    disabled?: boolean\n    /** Fires when the selection changes. */\n    onSelectedChange?: (value: string[]) => void\n    children?: Snippet<[ChipGroupSlotArgs]>\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let {\n    class: className,\n    selected = $bindable([]),\n    multiple = false,\n    filter = false,\n    column = false,\n    mandatory = false,\n    max,\n    disabled = false,\n    onSelectedChange,\n    children,\n    ...restProps\n  }: ChipGroupProps = $props()\n\n  function isSelected(value: string) {\n    return selected.includes(value)\n  }\n\n  function toggle(value: string) {\n    if (disabled) return\n\n    let newSelected: string[]\n\n    if (multiple) {\n      if (isSelected(value)) {\n        newSelected = selected.filter((v) => v !== value)\n      } else {\n        if (max && selected.length >= max) {\n          newSelected = [...selected.slice(1), value]\n        } else {\n          newSelected = [...selected, value]\n        }\n      }\n    } else {\n      if (isSelected(value) && !mandatory) {\n        newSelected = []\n      } else {\n        newSelected = [value]\n      }\n    }\n\n    selected = newSelected\n    onSelectedChange?.(newSelected)\n  }\n</script>\n\n<div\n  role=\"group\"\n  data-uipkge\n  data-slot=\"chip-group\"\n  data-chip-group=\"true\"\n  data-multiple={multiple || undefined}\n  data-filter={filter || undefined}\n  class={cn('flex flex-wrap gap-2', column && 'flex-col', className)}\n  {...restProps}\n>\n  {@render children?.({ selected, multiple, filter, isSelected, toggle })}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/chip/ChipGroup.svelte"
    },
    {
      "path": "packages/registry-svelte/components/chip/chip.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 chipVariants = cva(\n  // text-overflow/ellipsis cannot apply to a flex container's anonymous text,\n  // so truncation is opt-in via a child span (`<Chip><span class=\"truncate\">\n  // …</span></Chip>`); min-w-0 lets that span shrink inside the flex root.\n  'inline-flex items-center justify-center gap-1 rounded-full text-xs font-medium w-fit max-w-full whitespace-nowrap shrink-0 [&>span]:min-w-0 transition-colors duration-200 overflow-hidden focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n  {\n    variants: {\n      variant: {\n        default: 'bg-muted text-muted-foreground hover:bg-muted/80',\n        filled: 'bg-primary text-primary-foreground hover:bg-primary/90',\n        outlined: 'border border-current bg-transparent hover:bg-accent',\n        outline: 'border border-current bg-transparent hover:bg-accent',\n        elevated: 'bg-primary/10 text-primary shadow-sm hover:bg-primary/20',\n        success: 'bg-success/10 text-success dark:text-success hover:bg-success/20',\n        warning: 'bg-warning/10 text-warning dark:text-warning hover:bg-warning/20',\n        destructive: 'bg-destructive/10 text-destructive dark:text-destructive hover:bg-destructive/20',\n      },\n      size: {\n        sm: 'h-6 px-2 text-xs',\n        default: 'h-7 px-2.5 text-xs',\n        lg: 'h-8 px-3 text-sm',\n      },\n      // Allow multi-line labels. rounded-lg + extra leading keeps a wrapped\n      // chip readable instead of a cramped two-line stadium pill.\n      wrap: {\n        true: 'whitespace-normal h-auto rounded-lg py-1 leading-snug',\n      },\n    },\n    defaultVariants: {\n      variant: 'default',\n      size: 'default',\n    },\n  },\n)\n\nexport type ChipVariants = VariantProps<typeof chipVariants>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/chip/chip.variants.ts"
    },
    {
      "path": "packages/registry-svelte/components/chip/index.ts",
      "content": "export { default as Chip, type ChipProps } from './Chip.svelte'\nexport {\n  default as ChipGroup,\n  type ChipGroupProps,\n  type ChipGroupSlotArgs,\n  type ChipGroupRenderProps,\n} from './ChipGroup.svelte'\n\n// Re-export variant API from the sibling file (kept separate to avoid the\n// Chip.svelte <-> index.ts circular import that broke dev SSR for Card).\nexport { chipVariants, type ChipVariants } from './chip.variants'\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/chip/index.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority",
    "@lucide/svelte"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Compact, removable tag — typically used inside `tags-input` or as a filter pill. Seven variants (`default`, `filled`, `outlined`, `elevated`, `success`, `warning`, `destructive`), three sizes, an optional close button with enter/leave micro-animation, and a ChipGroup selection container with single/multiple/mandatory/max modes.",
  "categories": [
    "data-display"
  ]
}