{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "checkbox-list",
  "title": "List checkbox",
  "description": "Styled checkbox: the List set. 5 decorative checkbox variations (RowCheckbox, DividedCheckbox, GroupedCheckbox, InlineCheckbox, DenseCheckbox) on the ai2 token system, powered by framer-motion, reduced-motion aware and sized sm to xl. Part of the free styled layer.",
  "dependencies": [
    "motion@^12.42.2",
    "lucide-react@^1.23.0"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/checkbox-list.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Check } from \"lucide-react\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* List checkbox family: 5 list/group layouts. The box stays the same in every\n   variant; what changes is the shell of the row (a full-width row, a row with a\n   divider, a boxed group, an inline chip, a dense row). The whole row is the\n   clickable target, but because the box is under 24px it keeps the invisible\n   touch-area extension (after:-inset-1.5). The label is visible and bound with\n   aria-labelledby; the ids are produced by React.useId(). Color comes ONLY from\n   tokens, via alpha color-mix. framer-motion switches instantly under\n   reduced-motion. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nconst boxSize: Record<StyledSize, string> = {\n  sm: \"size-4\",\n  md: \"size-5\",\n  lg: \"size-6\",\n  xl: \"size-7\",\n}\n\nconst iconSize: Record<StyledSize, string> = {\n  sm: \"size-3\",\n  md: \"size-3.5\",\n  lg: \"size-4\",\n  xl: \"size-5\",\n}\n\nconst textSize: Record<StyledSize, string> = {\n  sm: \"text-xs\",\n  md: \"text-sm\",\n  lg: \"text-sm\",\n  xl: \"text-base\",\n}\n\nconst rowPad: Record<StyledSize, string> = {\n  sm: \"gap-2 px-2 py-1.5\",\n  md: \"gap-2.5 px-3 py-2\",\n  lg: \"gap-3 px-3 py-2.5\",\n  xl: \"gap-3 px-4 py-3\",\n}\n\nconst densePad: Record<StyledSize, string> = {\n  sm: \"gap-2 px-1.5 py-0.5\",\n  md: \"gap-2 px-2 py-1\",\n  lg: \"gap-2.5 px-2 py-1\",\n  xl: \"gap-2.5 px-2.5 py-1.5\",\n}\n\n/* Satir kabugu: genis hedef, focus halkasi, gecerlilik gorunumu. */\nconst rowBase =\n  \"relative flex w-full max-w-xs cursor-pointer select-none items-center text-left outline-none transition-colors focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-danger aria-invalid:ring-danger/20 dark:aria-invalid:ring-danger/40 [&_svg]:shrink-0 [&_i]:leading-none\"\n\n/* Kutu: kucuk gosterge, dokunma alani uzantisi korunur. */\nconst boxBase =\n  \"relative inline-flex shrink-0 items-center justify-center rounded-md border transition-colors after:absolute after:-inset-1.5\"\n\n/* Transition type: instant under reduced-motion, spring otherwise. */\ntype SpringLike = { duration: number } | { type: \"spring\"; stiffness: number; damping: number }\n\ntype CheckboxProps = Omit<React.ComponentProps<\"button\">, \"onChange\"> & {\n  size?: StyledSize\n  label?: string\n  checked?: boolean\n  defaultChecked?: boolean\n  onCheckedChange?: (checked: boolean) => void\n}\n\nfunction useCheckbox(props: {\n  checked?: boolean\n  defaultChecked?: boolean\n  onCheckedChange?: (checked: boolean) => void\n}) {\n  const { checked, defaultChecked, onCheckedChange } = props\n  const reduce = useReducedMotion()\n  const [internal, setInternal] = React.useState(defaultChecked ?? false)\n  const on = checked ?? internal\n  const toggle = () => {\n    if (checked === undefined) setInternal((v) => !v)\n    onCheckedChange?.(!on)\n  }\n  const spring: SpringLike = reduce\n    ? { duration: 0 }\n    : { type: \"spring\" as const, stiffness: 500, damping: 30 }\n  return { on, toggle, spring }\n}\n\n/* Ortak kutu gostergesi. */\nfunction Box({ on, size, spring }: { on: boolean; size: StyledSize; spring: SpringLike }) {\n  return (\n    <span\n      aria-hidden=\"true\"\n      className={cn(\n        boxBase,\n        boxSize[size],\n        on ? \"border-primary bg-primary text-primary-foreground\" : \"border-field-border text-transparent\"\n      )}\n    >\n      <motion.span\n        initial={false}\n        animate={{ scale: on ? 1 : 0, opacity: on ? 1 : 0 }}\n        transition={spring}\n        className=\"flex items-center justify-center\"\n      >\n        <Check className={cn(iconSize[size], \"stroke-[3]\")} />\n      </motion.span>\n    </span>\n  )\n}\n\n/* Row: a full-width list row that takes a background on hover. */\nexport function RowCheckbox({\n  className,\n  size = \"md\",\n  label = \"Marketing emails\",\n  checked,\n  defaultChecked,\n  onCheckedChange,\n  ...props\n}: CheckboxProps) {\n  const { on, toggle, spring } = useCheckbox({ checked, defaultChecked, onCheckedChange })\n  const labelId = React.useId()\n  return (\n    <button\n      type=\"button\"\n      role=\"checkbox\"\n      aria-checked={on}\n      aria-labelledby={labelId}\n      data-slot=\"styled-checkbox\"\n      onClick={toggle}\n      className={cn(\n        rowBase,\n        rowPad[size],\n        \"rounded-md hover:bg-[color-mix(in_oklab,var(--color-foreground)_5%,transparent)]\",\n        className\n      )}\n      {...props}\n    >\n      <Box on={on} size={size} spring={spring} />\n      <span id={labelId} className={cn(textSize[size], \"text-foreground\")}>\n        {label}\n      </span>\n    </button>\n  )\n}\n\n/* Divided: a row carrying a thin separator underneath; they stack. */\nexport function DividedCheckbox({\n  className,\n  size = \"md\",\n  label = \"Product updates\",\n  checked,\n  defaultChecked,\n  onCheckedChange,\n  ...props\n}: CheckboxProps) {\n  const { on, toggle, spring } = useCheckbox({ checked, defaultChecked, onCheckedChange })\n  const labelId = React.useId()\n  return (\n    <button\n      type=\"button\"\n      role=\"checkbox\"\n      aria-checked={on}\n      aria-labelledby={labelId}\n      data-slot=\"styled-checkbox\"\n      onClick={toggle}\n      className={cn(\n        rowBase,\n        rowPad[size],\n        \"border-b border-border px-0 hover:bg-[color-mix(in_oklab,var(--color-foreground)_4%,transparent)]\",\n        className\n      )}\n      {...props}\n    >\n      <Box on={on} size={size} spring={spring} />\n      <span id={labelId} className={cn(textSize[size], \"text-foreground\")}>\n        {label}\n      </span>\n      <span\n        aria-hidden=\"true\"\n        className={cn(textSize[size], \"ml-auto text-muted-foreground\")}\n      >\n        {on ? \"On\" : \"Off\"}\n      </span>\n    </button>\n  )\n}\n\n/* Grouped: a row sitting in its own bordered box, for grouped lists. */\nexport function GroupedCheckbox({\n  className,\n  size = \"md\",\n  label = \"Two factor auth\",\n  checked,\n  defaultChecked,\n  onCheckedChange,\n  ...props\n}: CheckboxProps) {\n  const { on, toggle, spring } = useCheckbox({ checked, defaultChecked, onCheckedChange })\n  const labelId = React.useId()\n  return (\n    <button\n      type=\"button\"\n      role=\"checkbox\"\n      aria-checked={on}\n      aria-labelledby={labelId}\n      data-slot=\"styled-checkbox\"\n      onClick={toggle}\n      className={cn(\n        rowBase,\n        rowPad[size],\n        \"rounded-lg border border-border bg-card first:rounded-b-none last:rounded-t-none [&:not(:first-child)]:-mt-px hover:bg-[color-mix(in_oklab,var(--color-foreground)_4%,transparent)]\",\n        className\n      )}\n      {...props}\n    >\n      <Box on={on} size={size} spring={spring} />\n      <span id={labelId} className={cn(textSize[size], \"font-medium text-foreground\")}>\n        {label}\n      </span>\n    </button>\n  )\n}\n\n/* Inline: an inline chip; for filter groups that flow side by side. */\nexport function InlineCheckbox({\n  className,\n  size = \"md\",\n  label = \"In stock\",\n  checked,\n  defaultChecked,\n  onCheckedChange,\n  ...props\n}: CheckboxProps) {\n  const { on, toggle, spring } = useCheckbox({ checked, defaultChecked, onCheckedChange })\n  const labelId = React.useId()\n  return (\n    <button\n      type=\"button\"\n      role=\"checkbox\"\n      aria-checked={on}\n      aria-labelledby={labelId}\n      data-slot=\"styled-checkbox\"\n      onClick={toggle}\n      className={cn(\n        rowBase,\n        rowPad[size],\n        \"inline-flex w-auto max-w-none rounded-full border\",\n        on\n          ? \"border-primary bg-[color-mix(in_oklab,var(--color-primary)_10%,transparent)]\"\n          : \"border-border bg-transparent hover:bg-[color-mix(in_oklab,var(--color-foreground)_5%,transparent)]\",\n        className\n      )}\n      {...props}\n    >\n      <Box on={on} size={size} spring={spring} />\n      <span id={labelId} className={cn(textSize[size], \"whitespace-nowrap text-foreground\")}>\n        {label}\n      </span>\n    </button>\n  )\n}\n\n/* Dense: a tightened row for dense tables and long lists. */\nexport function DenseCheckbox({\n  className,\n  size = \"md\",\n  label = \"Show archived\",\n  checked,\n  defaultChecked,\n  onCheckedChange,\n  ...props\n}: CheckboxProps) {\n  const { on, toggle, spring } = useCheckbox({ checked, defaultChecked, onCheckedChange })\n  const labelId = React.useId()\n  return (\n    <button\n      type=\"button\"\n      role=\"checkbox\"\n      aria-checked={on}\n      aria-labelledby={labelId}\n      data-slot=\"styled-checkbox\"\n      onClick={toggle}\n      className={cn(\n        rowBase,\n        densePad[size],\n        \"rounded hover:bg-[color-mix(in_oklab,var(--color-foreground)_5%,transparent)]\",\n        className\n      )}\n      {...props}\n    >\n      <Box on={on} size={size} spring={spring} />\n      <span id={labelId} className={cn(textSize[size], \"leading-tight text-muted-foreground\")}>\n        {label}\n      </span>\n    </button>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/checkbox-list.tsx"
    }
  ],
  "categories": [
    "styled",
    "checkbox"
  ],
  "type": "registry:component"
}