{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "checkbox",
  "title": "Checkbox",
  "description": "A small square that fills with the accent when checked.",
  "dependencies": [
    "@base-ui/react",
    "@hugeicons/core-free-icons",
    "@hugeicons/react",
    "@stylexjs/stylex"
  ],
  "registryDependencies": [
    "https://constella.aayush.cv/r/base.json"
  ],
  "files": [
    {
      "path": "src/components/primitives/checkbox.tsx",
      "content": "\"use client\";\n\nimport { Checkbox as CheckboxPrimitive } from \"@base-ui/react/checkbox\";\nimport { MinusSignIcon, Tick02Icon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport * as stylex from \"@stylexjs/stylex\";\n\nimport type { Sx } from \"@/lib/constella/styles\";\nimport {\n  colors,\n  durations,\n  easings,\n  lineHeights,\n  media,\n  opacities,\n  presses,\n  radii,\n  shadows,\n  sizes,\n  strokes,\n} from \"@/lib/constella/tokens.stylex\";\n\nconst checkboxSizes = [\"sm\", \"default\", \"lg\"] as const;\n\ntype CheckboxSize = (typeof checkboxSizes)[number];\n\ntype CheckboxProps = Omit<\n  CheckboxPrimitive.Root.Props,\n  \"className\" | \"style\"\n> & {\n  size?: CheckboxSize;\n  sx?: Sx;\n};\n\nconst on = \":is([data-checked], [data-indeterminate])\";\nconst invalid = \":is([aria-invalid='true'], [data-invalid])\";\n\n// The box, centered on the label's first line beside top-aligned text, with its hit area.\nconst box = (size: string) => ({\n  height: size,\n  marginBlock: {\n    default: null,\n    \":is([data-orientation='horizontal'] > *, [data-slot='field-label'] > *)\": `calc((${lineHeights.textSm} - ${size}) / 2)`,\n  },\n  width: size,\n  \"::before\": {\n    insetBlock: `calc((${size} - ${sizes.hitArea}) / 2)`,\n    insetInline: `calc((${size} - ${sizes.hitArea}) / 2)`,\n  },\n});\n\nconst styles = stylex.create({\n  checkbox: {\n    alignItems: \"center\",\n    // The checked edge is opaque, so the fill runs under it; clipped, it leaves a seam.\n    backgroundClip: { default: \"padding-box\", [on]: \"border-box\" },\n    backgroundColor: { default: colors.fillSubtle, [on]: colors.infoSolid },\n    borderBlockColor: {\n      default: colors.borderStrong,\n      [invalid]: colors.dangerBorder,\n      [on]: colors.infoSolid,\n    },\n    borderInlineColor: {\n      default: colors.borderStrong,\n      [invalid]: colors.dangerBorder,\n      [on]: colors.infoSolid,\n    },\n    borderStartStartRadius: radii.xs,\n    borderStartEndRadius: radii.xs,\n    borderEndStartRadius: radii.xs,\n    borderEndEndRadius: radii.xs,\n    borderBlockStyle: \"solid\",\n    borderInlineStyle: \"solid\",\n    borderBlockWidth: strokes.border,\n    borderInlineWidth: strokes.border,\n    boxShadow: { default: shadows.default, [invalid]: shadows.defaultInvalid },\n    color: colors.infoOnSolid,\n    cursor: { default: \"pointer\", \":is([data-disabled])\": \"not-allowed\" },\n    display: \"inline-flex\",\n    flexShrink: 0,\n    justifyContent: \"center\",\n    opacity: { default: 1, \":is([data-disabled])\": opacities.disabled },\n    position: \"relative\",\n    transform: {\n      default: null,\n      \":active:not([data-disabled])\": {\n        default: presses.icon,\n        [media.reducedMotion]: null,\n      },\n    },\n    transitionDuration: `${durations.hover}, ${durations.hover}, ${durations.press}`,\n    transitionProperty: \"background-color, border-color, transform\",\n    transitionTimingFunction: `ease, ease, ${easings.out}`,\n    verticalAlign: \"middle\",\n    \"::before\": {\n      content: \"''\",\n      position: \"absolute\",\n    },\n  },\n  sm: box(sizes.iconSm),\n  default: box(sizes.icon),\n  lg: box(sizes.iconLg),\n  // The tick draws in from its start and fades out; unchecking is the system answering, so it's quick.\n  // Toggled from the keyboard (only a keyboard focus matches `:focus-visible`), it doesn't move.\n  indicator: {\n    clipPath: {\n      default: \"inset(0)\",\n      \":is([data-starting-style])\": \"inset(0 100% 0 0)\",\n    },\n    display: \"flex\",\n    opacity: { default: 1, \":is([data-ending-style])\": 0 },\n    transitionDuration: {\n      default: durations.move,\n      \":is([data-ending-style]):not(:focus-visible > *)\": durations.hover,\n      \":is(:focus-visible > *)\": \"0s\",\n    },\n    transitionProperty: {\n      default: \"clip-path, opacity\",\n      [media.reducedMotion]: \"opacity\",\n    },\n    transitionTimingFunction: easings.out,\n  },\n  tick: {\n    display: { default: \"block\", \":is([data-indeterminate] *)\": \"none\" },\n  },\n  minus: {\n    display: { default: \"none\", \":is([data-indeterminate] *)\": \"block\" },\n  },\n});\n\nconst sizeStyles = {\n  default: styles.default,\n  lg: styles.lg,\n  sm: styles.sm,\n} satisfies Record<CheckboxSize, stylex.StyleXStyles>;\n\n// The tick is a step under the box.\nconst tickSizes = {\n  default: sizes.iconSm,\n  lg: sizes.icon,\n  sm: sizes.iconXs,\n} satisfies Record<CheckboxSize, string>;\n\nfunction Checkbox({ size = \"default\", sx, ...props }: CheckboxProps) {\n  return (\n    <CheckboxPrimitive.Root\n      data-size={size}\n      data-slot=\"checkbox\"\n      {...props}\n      {...stylex.props(styles.checkbox, sizeStyles[size], sx)}\n    >\n      <CheckboxPrimitive.Indicator\n        data-slot=\"checkbox-indicator\"\n        {...stylex.props(styles.indicator)}\n      >\n        <HugeiconsIcon\n          aria-hidden\n          icon={Tick02Icon}\n          size={tickSizes[size]}\n          strokeWidth={Number(strokes.icon)}\n          {...stylex.props(styles.tick)}\n        />\n        <HugeiconsIcon\n          aria-hidden\n          icon={MinusSignIcon}\n          size={tickSizes[size]}\n          strokeWidth={Number(strokes.icon)}\n          {...stylex.props(styles.minus)}\n        />\n      </CheckboxPrimitive.Indicator>\n    </CheckboxPrimitive.Root>\n  );\n}\n\nexport { Checkbox, checkboxSizes };\nexport type { CheckboxProps, CheckboxSize };\n",
      "type": "registry:component"
    }
  ],
  "docs": "Usage: https://constella.aayush.cv/docs/components/checkbox/index.md",
  "type": "registry:ui"
}