{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "checkbox",
  "title": "Checkbox",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-svelte/components/checkbox/Checkbox.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLButtonAttributes, MouseEventHandler } from 'svelte/elements'\n\n  export type CheckboxChecked = boolean | 'indeterminate'\n\n  export interface CheckboxProps extends Omit<HTMLButtonAttributes, 'value' | 'children'> {\n    /** The controlled checked state. Bindable (`bind:checked`); falls back to internal state when unbound. */\n    checked?: CheckboxChecked | null\n    /** The value given as data when submitted with a name. Inside a group, drives membership. */\n    value?: string\n    /** Id of the element. Auto-generated when `label` is set without an explicit `id`. */\n    id?: string\n    /** Size of the checkbox. */\n    size?: 'sm' | 'md' | 'lg'\n    /** Custom color for the checked state — matches the Vuetify color system. */\n    color?: 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info' | string\n    /** Label text displayed next to the checkbox. */\n    label?: string\n    /** Hint text shown below the checkbox. */\n    hint?: string\n    /** Error message(s) to display. */\n    errorMessages?: string | string[]\n    /** Whether to show error state. */\n    error?: boolean\n    /** When `true`, the checkbox is in readonly state (focusable but not toggleable). */\n    readonly?: boolean\n    /** When `true`, shows an indeterminate state. */\n    indeterminate?: boolean\n    /** Density of the checkbox — affects spacing. */\n    density?: 'compact' | 'default' | 'comfortable'\n    /** When `true`, the icon is hidden. */\n    hideIcon?: boolean\n    /** Loading state — shows a spinner. */\n    loading?: boolean\n    /** Ripple effect on click (accepted for API parity; no visual effect). */\n    ripple?: boolean\n    /** Custom icon to show when checked. */\n    checkedIcon?: Snippet\n    /** Custom icon to show when indeterminate. */\n    indeterminateIcon?: Snippet\n    /** Label position — before or after the checkbox. */\n    labelPosition?: 'before' | 'after'\n    /** Whether the checkbox appears flat (no elevation). */\n    flat?: boolean\n    /** Inline text style. */\n    inline?: boolean\n    /** Name attribute for form submission. */\n    name?: string\n    /** The rendered control, via `bind:ref`. */\n    ref?: HTMLButtonElement | null\n    /** Custom indicator content. Receives `{ checked }`. */\n    children?: Snippet<[{ checked: CheckboxChecked }]>\n    /** Fires when the checked state changes (click or keyboard toggle). */\n    onCheckedChange?: (value: CheckboxChecked) => void\n  }\n</script>\n\n<script lang=\"ts\">\n  import { getContext } from 'svelte'\n  import { Check, Loader2, Minus } from '@lucide/svelte'\n  import { cn } from '$lib/utils'\n  import type { CheckboxGroupContext } from './CheckboxGroup.svelte'\n\n  let {\n    class: className,\n    checked = $bindable(false),\n    value,\n    id,\n    size = 'md',\n    color = 'primary',\n    label,\n    hint,\n    errorMessages,\n    error = false,\n    disabled = false,\n    readonly = false,\n    indeterminate = false,\n    density = 'default',\n    hideIcon = false,\n    loading = false,\n    ripple: _ripple = true,\n    checkedIcon,\n    indeterminateIcon,\n    labelPosition = 'after',\n    flat = false,\n    inline = false,\n    name,\n    ref = $bindable(null),\n    children,\n    onCheckedChange,\n    onclick: onclickProp,\n    ...restProps\n  }: CheckboxProps = $props()\n\n  // `_ripple` is destructured aside (accepted for API parity with the Vue twin)\n  // so it never leaks into `...restProps` and onto the DOM.\n  const group = getContext<CheckboxGroupContext | null>('checkboxGroupContext') ?? null\n  const inGroup = $derived(group !== null && value !== undefined)\n  const actualName = $derived(name ?? group?.name)\n\n  // Stable auto id for the `<label for>` association (no reactive useId in\n  // Svelte — a module counter is enough for uniqueness within a page).\n  let autoIdCounter = 0\n  const autoId = `checkbox-${++autoIdCounter}-${Math.random().toString(36).slice(2, 7)}`\n  const resolvedId = $derived(id ?? (label ? autoId : undefined))\n\n  const sizeClasses = {\n    sm: 'size-3.5',\n    md: 'size-4',\n    lg: 'size-5',\n  } as const\n\n  const iconSizes = {\n    sm: 'size-2.5',\n    md: 'size-3.5',\n    lg: 'size-4',\n  } as const\n\n  const colorClasses: Record<string, string> = {\n    primary:\n      'data-[state=checked]:bg-primary data-[state=checked]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:border-primary',\n    secondary:\n      'data-[state=checked]:bg-secondary data-[state=checked]:border-secondary data-[state=indeterminate]:bg-secondary data-[state=indeterminate]:border-secondary',\n    success:\n      'data-[state=checked]:bg-success data-[state=checked]:border-success data-[state=indeterminate]:bg-success data-[state=indeterminate]:border-success',\n    warning:\n      'data-[state=checked]:bg-warning data-[state=checked]:border-warning data-[state=indeterminate]:bg-warning data-[state=indeterminate]:border-warning',\n    error:\n      'data-[state=checked]:bg-destructive data-[state=checked]:border-destructive data-[state=indeterminate]:bg-destructive data-[state=indeterminate]:border-destructive',\n    info: 'data-[state=checked]:bg-info data-[state=checked]:border-info data-[state=indeterminate]:bg-info data-[state=indeterminate]:border-info',\n  }\n\n  const densityClasses = {\n    compact: 'gap-1',\n    default: 'gap-2',\n    comfortable: 'gap-3',\n  } as const\n\n  const hasError = $derived(\n    Boolean(error || (typeof errorMessages === 'string' ? errorMessages : (errorMessages?.length ?? 0) > 0)),\n  )\n\n  const groupChecked = $derived(inGroup ? group!.isSelected(value!) : null)\n  const isIndeterminate = $derived(indeterminate || checked === 'indeterminate')\n  const dataState = $derived<'checked' | 'unchecked' | 'indeterminate'>(\n    isIndeterminate ? 'indeterminate' : (groupChecked ?? checked ?? false) ? 'checked' : 'unchecked',\n  )\n  const ariaChecked = $derived(dataState === 'indeterminate' ? 'mixed' : dataState === 'checked')\n\n  const checkboxClasses = $derived(\n    cn(\n      'peer border-input data-[state=checked]:text-primary-foreground data-[state=indeterminate]:text-primary-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive shrink-0 rounded-[4px] border shadow-xs transition-colors duration-200 outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',\n      sizeClasses[size],\n      colorClasses[color] || colorClasses.primary,\n      hasError &&\n        'border-destructive data-[state=checked]:!bg-destructive data-[state=checked]:!border-destructive data-[state=indeterminate]:!bg-destructive data-[state=indeterminate]:!border-destructive',\n      flat && 'shadow-none',\n      className,\n    ),\n  )\n\n  const handleClick: MouseEventHandler<HTMLButtonElement> = (event) => {\n    toggle()\n    onclickProp?.(event)\n  }\n\n  function toggle() {\n    if (disabled || group?.disabled || readonly || loading) return\n    if (inGroup) {\n      group!.toggle(value!)\n      onCheckedChange?.(group!.isSelected(value!) ? true : false)\n      return\n    }\n    const next: CheckboxChecked = dataState === 'checked' ? false : true\n    checked = next\n    onCheckedChange?.(next)\n  }\n</script>\n\n<div class={cn('flex items-start', densityClasses[density], inline ? 'inline-flex' : 'flex-col')}>\n  {#if label && labelPosition === 'before'}\n    <label\n      for={resolvedId}\n      class={cn(\n        'mr-2 cursor-pointer text-sm leading-none font-medium select-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',\n        hasError ? 'text-destructive' : '',\n        disabled && 'cursor-not-allowed opacity-50',\n      )}\n    >\n      {label}\n    </label>\n  {/if}\n\n  <div class=\"flex items-center\">\n    <button\n      bind:this={ref}\n      type=\"button\"\n      role=\"checkbox\"\n      data-uipkge\n      data-slot=\"checkbox\"\n      id={resolvedId}\n      aria-checked={ariaChecked}\n      aria-invalid={hasError || undefined}\n      data-state={dataState}\n      {value}\n      disabled={disabled || group?.disabled}\n      class={checkboxClasses}\n      onclick={handleClick}\n      {...restProps}\n    >\n      <span data-slot=\"checkbox-indicator\" class=\"grid place-content-center text-current transition-none\">\n        {#if children}\n          {@render children({\n            checked: dataState === 'checked' ? true : dataState === 'indeterminate' ? 'indeterminate' : false,\n          })}\n        {:else if loading}\n          <Loader2 class={cn(iconSizes[size], 'animate-spin')} />\n        {:else if isIndeterminate}\n          {#if indeterminateIcon}\n            {@render indeterminateIcon()}\n          {:else}\n            <Minus class={cn(iconSizes[size], 'checkbox-indicator-icon')} />\n          {/if}\n        {:else if !hideIcon && dataState === 'checked'}\n          {#if checkedIcon}\n            {@render checkedIcon()}\n          {:else}\n            <Check class={cn(iconSizes[size], 'checkbox-indicator-icon')} />\n          {/if}\n        {/if}\n      </span>\n    </button>\n    {#if actualName}\n      <input\n        type=\"checkbox\"\n        hidden\n        tabindex=\"-1\"\n        aria-hidden=\"true\"\n        name={actualName}\n        value={value ?? 'on'}\n        checked={dataState === 'checked'}\n      />\n    {/if}\n\n    {#if label && labelPosition === 'after'}\n      <label\n        for={resolvedId}\n        class={cn(\n          'ml-2 cursor-pointer text-sm leading-none font-medium select-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',\n          hasError ? 'text-destructive' : '',\n          disabled && 'cursor-not-allowed opacity-50',\n        )}\n      >\n        {label}\n      </label>\n    {/if}\n  </div>\n\n  {#if hint && !hasError}\n    <p class=\"text-muted-foreground mt-1 text-xs\">\n      {hint}\n    </p>\n  {/if}\n\n  {#if hasError}\n    <div class=\"mt-1 flex flex-col gap-0.5\">\n      {#if typeof errorMessages === 'string'}\n        <p class=\"text-destructive text-xs\">{errorMessages}</p>\n      {:else}\n        {#each errorMessages ?? [] as msg (msg)}\n          <p class=\"text-destructive text-xs\">{msg}</p>\n        {/each}\n      {/if}\n    </div>\n  {/if}\n</div>\n\n<style>\n  /* Subtle scale-in when the check / indeterminate mark mounts. */\n  @keyframes checkbox-check-in {\n    0% {\n      opacity: 0;\n      transform: scale(0.55);\n    }\n    70% {\n      opacity: 1;\n      transform: scale(1.08);\n    }\n    100% {\n      opacity: 1;\n      transform: scale(1);\n    }\n  }\n\n  :global([data-slot='checkbox-indicator']) .checkbox-indicator-icon {\n    animation: checkbox-check-in 200ms cubic-bezier(0.22, 1.2, 0.36, 1) both;\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    :global([data-slot='checkbox-indicator']) .checkbox-indicator-icon {\n      animation: none !important;\n    }\n  }\n</style>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/checkbox/Checkbox.svelte"
    },
    {
      "path": "packages/registry-svelte/components/checkbox/CheckboxGroup.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface CheckboxOption {\n    label: string\n    value: string\n    disabled?: boolean\n  }\n\n  export interface CheckboxGroupProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {\n    /** Checked values. Bindable (`bind:value`). */\n    value?: string[]\n    /** Initial checked values when `value` is unbound. */\n    defaultValue?: string[]\n    /** When `true`, prevents interaction with all checkboxes. */\n    disabled?: boolean\n    /** Whether the group is in an error state. */\n    error?: boolean\n    /** Error messages to display. */\n    errorMessages?: string | string[]\n    /** Label for the group. */\n    label?: string\n    /** Hint text for the group. */\n    hint?: string\n    /** The orientation of the checkboxes. */\n    orientation?: 'horizontal' | 'vertical'\n    /** Whether to show a border around the group. */\n    bordered?: boolean\n    /** Density of the checkboxes. */\n    density?: 'compact' | 'default' | 'comfortable'\n    /** Options to render as checkboxes automatically. */\n    options?: (string | CheckboxOption)[]\n    /** Name attribute for all checkboxes in the group. */\n    name?: string\n    /** Inline layout (alias for horizontal). */\n    inline?: boolean\n    /** Fires when the checked values change. */\n    onValueChange?: (value: string[]) => void\n    children?: Snippet<[{ value: string[] }]>\n  }\n\n  /** Hand-rolled group context (replaces reka-ui's CheckboxGroupRoot state). */\n  export interface CheckboxGroupContext {\n    readonly name: string | undefined\n    readonly disabled: boolean\n    isSelected: (value: string) => boolean\n    toggle: (value: string) => void\n  }\n</script>\n\n<script lang=\"ts\">\n  import { setContext } from 'svelte'\n  import { cn } from '$lib/utils'\n  import Checkbox from './Checkbox.svelte'\n\n  let {\n    class: className,\n    value = $bindable(),\n    defaultValue = [],\n    disabled = false,\n    error = false,\n    errorMessages,\n    label,\n    hint,\n    orientation = 'vertical',\n    bordered = false,\n    density = 'default',\n    options,\n    name,\n    inline = false,\n    onValueChange,\n    children,\n    ...restProps\n  }: CheckboxGroupProps = $props()\n\n  // Intentional one-shot seed: later `defaultValue` changes must not clobber user selection.\n  // svelte-ignore state_referenced_locally\n  let internalValue = $state<string[]>([...defaultValue])\n  const resolved = $derived(value ?? internalValue)\n\n  const actualOrientation = $derived(inline ? 'horizontal' : orientation)\n\n  function isSelected(v: string) {\n    return resolved.includes(v)\n  }\n\n  function toggle(v: string) {\n    if (disabled) return\n    const next = isSelected(v) ? resolved.filter((item) => item !== v) : [...resolved, v]\n    if (value === undefined) internalValue = next\n    value = next\n    onValueChange?.(next)\n  }\n\n  setContext<CheckboxGroupContext>('checkboxGroupContext', {\n    get name() {\n      return name\n    },\n    get disabled() {\n      return disabled\n    },\n    isSelected,\n    toggle,\n  })\n</script>\n\n<div\n  role=\"group\"\n  data-uipkge\n  data-slot=\"checkbox-group\"\n  data-orientation={actualOrientation}\n  class={cn(\n    'flex flex-col gap-2',\n    actualOrientation === 'horizontal' ? 'flex-row items-center' : 'flex-col',\n    bordered && 'rounded-lg border p-4',\n    className,\n  )}\n  {...restProps}\n>\n  {#if label}\n    <span class=\"text-sm font-medium\">{label}</span>\n  {/if}\n\n  {#if hint && !error}\n    <p class=\"text-muted-foreground text-xs\">{hint}</p>\n  {/if}\n\n  <div class={cn('flex gap-4', actualOrientation === 'horizontal' ? 'flex-row flex-wrap items-center' : 'flex-col')}>\n    {#if options && options.length > 0}\n      {#each options as option, i (typeof option === 'string' ? option : `${option.value}-${i}`)}\n        <Checkbox\n          value={typeof option === 'string' ? option : option.value}\n          label={typeof option === 'string' ? option : option.label}\n          disabled={disabled || (typeof option === 'string' ? undefined : option.disabled)}\n          {name}\n          {density}\n        />\n      {/each}\n    {:else if children}\n      {@render children({ value: resolved })}\n    {/if}\n  </div>\n\n  {#if error || errorMessages}\n    <div class=\"flex flex-col gap-0.5\">\n      {#if typeof errorMessages === 'string'}\n        <p class=\"text-destructive text-xs\">{errorMessages}</p>\n      {:else}\n        {#each errorMessages ?? [] as msg (msg)}\n          <p class=\"text-destructive text-xs\">{msg}</p>\n        {/each}\n      {/if}\n    </div>\n  {/if}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/checkbox/CheckboxGroup.svelte"
    },
    {
      "path": "packages/registry-svelte/components/checkbox/index.ts",
      "content": "export { default as Checkbox, type CheckboxChecked, type CheckboxProps } from './Checkbox.svelte'\nexport {\n  default as CheckboxGroup,\n  type CheckboxGroupContext,\n  type CheckboxGroupProps,\n  type CheckboxOption,\n} from './CheckboxGroup.svelte'\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/checkbox/index.ts"
    }
  ],
  "dependencies": [
    "@lucide/svelte"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Standalone or in-form binary toggle with Svelte 5 runes (no headless dependency — keyboard, tri-state, and group context are hand-rolled). Supports indeterminate state for tri-state lists, sizes, colors, label/hint/error plumbing, and proper keyboard / screen-reader behavior. CheckboxGroup renders option arrays or manages slotted checkboxes with bind:value.",
  "categories": [
    "form"
  ]
}