{
  "name": "checkbox",
  "layer": "base",
  "type": "component",
  "title": "Checkbox components",
  "description": "Free and open-source React checkbox components built for modern applications and websites. These checkboxes are built using React Aria and styled with Tailwind CSS.",
  "files": [
    {
      "path": "components/base/checkbox/checkbox.tsx",
      "target": "components/base/checkbox/checkbox.tsx",
      "type": "component",
      "content": "\"use client\";\n\nimport type { ReactNode, Ref } from \"react\";\nimport { useId } from \"react\";\nimport { Checkbox as AriaCheckbox, type CheckboxProps as AriaCheckboxProps } from \"react-aria-components\";\nimport { cx, sortCx } from \"@/utils/cx\";\nimport { warnDomProps } from \"@/utils/warn-dom-props\";\n\nexport interface CheckboxBaseProps {\n    /** The size of the checkbox. */\n    size?: \"sm\" | \"md\";\n    /** Additional CSS classes to apply to the root element. */\n    className?: string;\n    /** Whether the checkbox should show a visible focus ring. */\n    isFocusVisible?: boolean;\n    /** Whether the checkbox is selected. */\n    isSelected?: boolean;\n    /** Whether the checkbox is disabled. */\n    isDisabled?: boolean;\n    /** Whether the checkbox is in an indeterminate state. */\n    isIndeterminate?: boolean;\n}\n\nexport const CheckboxBase = ({ className, isSelected, isDisabled, isIndeterminate, size = \"sm\", isFocusVisible = false }: CheckboxBaseProps) => {\n    return (\n        <div\n            className={cx(\n                \"bg-primary ring-primary relative flex size-4 shrink-0 cursor-pointer appearance-none items-center justify-center rounded ring-1 ring-inset\",\n                size === \"md\" && \"size-5 rounded-md\",\n                (isSelected || isIndeterminate) && \"bg-brand-solid ring-brand-solid\",\n                isDisabled && \"cursor-not-allowed opacity-50\",\n                isDisabled && !(isSelected || isIndeterminate) && \"bg-tertiary\",\n                isFocusVisible && \"outline-focus-ring outline-2 outline-offset-2\",\n                className,\n            )}\n        >\n            <svg\n                aria-hidden=\"true\"\n                viewBox=\"0 0 14 14\"\n                fill=\"none\"\n                className={cx(\n                    \"text-fg-white transition-inherit-all pointer-events-none absolute h-3 w-2.5 opacity-0\",\n                    size === \"md\" && \"size-3.5\",\n                    isIndeterminate && \"opacity-100\",\n                )}\n            >\n                <path d=\"M2.91675 7H11.0834\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n            </svg>\n\n            <svg\n                aria-hidden=\"true\"\n                viewBox=\"0 0 14 14\"\n                fill=\"none\"\n                className={cx(\n                    \"text-fg-white transition-inherit-all pointer-events-none absolute size-3 opacity-0\",\n                    size === \"md\" && \"size-3.5\",\n                    isSelected && !isIndeterminate && \"opacity-100\",\n                )}\n            >\n                <path d=\"M11.6666 3.5L5.24992 9.91667L2.33325 7\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n            </svg>\n        </div>\n    );\n};\nCheckboxBase.displayName = \"CheckboxBase\";\n\nconst styles = sortCx({\n    sm: {\n        root: \"gap-2\",\n        textWrapper: \"\",\n        label: \"text-sm font-medium\",\n        hint: \"text-sm\",\n    },\n    md: {\n        root: \"gap-3\",\n        textWrapper: \"gap-0.5\",\n        label: \"text-md font-medium\",\n        hint: \"text-md\",\n    },\n});\n\nexport interface CheckboxProps extends AriaCheckboxProps {\n    /** Ref forwarded to the root `<label>` element. */\n    ref?: Ref<HTMLLabelElement>;\n    /** The size of the checkbox. */\n    size?: \"sm\" | \"md\";\n    /** The label rendered next to the checkbox. */\n    label?: ReactNode;\n    /** A supporting hint rendered below the label. */\n    hint?: ReactNode;\n}\n\nexport const Checkbox = ({ label, hint, size = \"sm\", className, ...ariaCheckboxProps }: CheckboxProps) => {\n    warnDomProps(\"Checkbox\", ariaCheckboxProps as Record<string, unknown>, { checked: \"isSelected\", disabled: \"isDisabled\", required: \"isRequired\" });\n\n    const generatedId = useId();\n    // A hint rendered *inside* the `<label>` React Aria's `Checkbox` produces becomes part of the\n    // input's accessible name (e.g. \"Remember me Save my login details…\"). Rendering it as a\n    // sibling and wiring it up with `aria-describedby` instead keeps the accessible name equal to\n    // just the label, while the hint is still announced as a description.\n    const hintId = hint ? `checkbox-hint-${generatedId}` : undefined;\n\n    return (\n        <div className=\"flex flex-col\">\n            <AriaCheckbox\n                // With a label, the hint describes; without one (a consent checkbox whose only\n                // text is the hint), the hint must still name the control or it has no\n                // accessible name at all, which is what the contact-form demos exercise.\n                aria-describedby={label ? hintId : undefined}\n                aria-labelledby={!label && hint ? hintId : undefined}\n                {...ariaCheckboxProps}\n                className={(state) =>\n                    cx(\n                        \"relative flex items-start\",\n                        state.isDisabled && \"cursor-not-allowed\",\n                        styles[size].root,\n                        typeof className === \"function\" ? className(state) : className,\n                    )\n                }\n            >\n                {({ isSelected, isIndeterminate, isDisabled, isFocusVisible }) => (\n                    <>\n                        <CheckboxBase\n                            size={size}\n                            isSelected={isSelected}\n                            isIndeterminate={isIndeterminate}\n                            isDisabled={isDisabled}\n                            isFocusVisible={isFocusVisible}\n                            className={label ? \"mt-0.5\" : \"\"}\n                        />\n                        {label && (\n                            <div className={cx(\"inline-flex flex-col\", styles[size].textWrapper)}>\n                                <p className={cx(\"text-secondary select-none\", styles[size].label)}>{label}</p>\n                            </div>\n                        )}\n                    </>\n                )}\n            </AriaCheckbox>\n\n            {hint && (\n                <span id={hintId} className={cx(\"text-tertiary\", styles[size].hint, size === \"sm\" ? \"ms-6\" : \"ms-8\")}>\n                    {hint}\n                </span>\n            )}\n        </div>\n    );\n};\nCheckbox.displayName = \"Checkbox\";\n",
      "dependencies": [
        "react",
        "react-aria-components"
      ]
    }
  ],
  "registryDependencies": [
    "cx",
    "warn-dom-props"
  ],
  "optionalRegistryDependencies": [],
  "dependencies": [
    "react",
    "react-aria-components"
  ],
  "cssVars": [],
  "examples": [
    "base",
    "checkbox-example",
    "disabled",
    "sizes",
    "with-label",
    "with-label-and-hint"
  ],
  "docs": "/components/checkboxes",
  "intent": "Capture a single independent on/off or tri-state choice within a form.",
  "avoid_when": [
    "choosing exactly one option from a mutually exclusive set (use radio-buttons)",
    "an immediate, form-free settings toggle (use toggle)"
  ],
  "composes_with": [
    "form",
    "input",
    "table"
  ],
  "a11y_contract": [
    "built on React Aria Checkbox: keyboard toggling (Space), checked/indeterminate/disabled state and `role=\"checkbox\"` come for free",
    "the visible label passed as children is associated with the control automatically; an icon-only checkbox still needs its own aria-label"
  ],
  "requires_data": [
    "a visible label describing what is being agreed to or selected"
  ],
  "token_contract": [
    "bg-brand-solid",
    "bg-primary",
    "bg-tertiary",
    "text-fg-white",
    "text-secondary",
    "text-tertiary"
  ],
  "changelog": [
    {
      "version": "0.3.0",
      "changes": [
        "Adds six new component groups. Under `base/`: `HoverCard` (a rich hover/focus-triggered preview built on `Popover`, for things a plain `Tooltip` can't hold, like a profile card with a Follow button), `Menubar` (an application-style `File`/`Edit`/`View` menu bar built on React Aria's `Toolbar` plus the same `Menu`/`MenuTrigger` primitives `Dropdown` uses, with hover-switching between open menus, submenus, and checkbox/radio items), `NumberInput` (a `NumberField`-based numeric input with stacked or inline increment/decrement buttons and `formatOptions` for currency/percent/unit display), and `TagInput` (type-to-add tags on Enter/comma, paste-splits- on-commas, `maxTags`, a `validate` callback, and `isReadOnly`). Under `application/`: `Stepper` (a controlled multi-step form wizard — distinct from the purely decorative `ProgressSteps` — with `canAdvance` validation, linear and non-linear navigation, and horizontal/vertical layouts) and `Timeline` (vertical, alternating and horizontal status timelines with `completed`/`current`/`upcoming` coloring)."
      ]
    },
    {
      "version": "0.2.0",
      "changes": [
        "Field API and accessible-name fixes from the agent feedback map (docs/spec/feedback/2026-09-11-agent-feedback-map.md items 2.17, 2.20, 2.22, and the JSDoc half of 2.11), batched across `input`, `select`, `checkbox`, `radio-buttons`, `badges`, `toggle`, `tags`, `buttons/button`, `date-picker`, `page-headers`, and `foundations/featured-icon`:"
      ]
    }
  ],
  "platforms": [
    "react",
    "next"
  ]
}
