{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"checkbox-clean","type":"registry:ui","title":"Checkbox (Clean)","description":"A real checkbox with a label and a second line, a some-of-them state for Select all, an error and a card row – the check draws itself","dependencies":[],"files":[{"path":"registry/checkbox-clean/checkbox-clean.tsx","type":"registry:ui","content":"\"use client\";\n\nimport { useEffect, useId, useRef, type ComponentPropsWithRef, type ReactNode } from \"react\";\n\n/*\n * Checkbox – a single self-contained file, clean style.\n * A real checkbox with a label and a quiet second line, a third \"some of them\" state, an error, and a card variant\n * where the whole row is the target. The box fills and its check draws itself. Focus shows a ring only from the\n * keyboard. The styles are injected below, so no extra files are needed.\n */\n\nexport type CheckboxProps = Omit<ComponentPropsWithRef<\"input\">, \"type\" | \"size\"> & {\n  /** Shown next to the box; clicking it toggles */\n  label?: ReactNode;\n  /** A quiet second line under the label */\n  description?: ReactNode;\n  /** \"Some of them\" – a dash instead of the check, e.g. for Select all */\n  indeterminate?: boolean;\n  /** Marks it invalid and shows this under it */\n  error?: ReactNode;\n  /** plain: the box and its text · card: a bordered row that is clickable as a whole and lights up when checked */\n  variant?: \"plain\" | \"card\";\n};\n\nexport function Checkbox({\n  label,\n  description,\n  indeterminate = false,\n  error,\n  variant = \"plain\",\n  className,\n  id,\n  disabled,\n  ref,\n  \"aria-describedby\": describedBy,\n  ...rest\n}: CheckboxProps) {\n  const autoId = useId();\n  const inputId = id ?? `${autoId}-input`;\n  const descId = `${autoId}-desc`;\n  const errorId = `${autoId}-error`;\n  const input = useRef<HTMLInputElement | null>(null);\n  const showsError = error != null && typeof error !== \"boolean\" && error !== \"\";\n\n  // Indeterminate is a property, not an attribute\n  useEffect(() => {\n    if (input.current) input.current.indeterminate = indeterminate;\n  }, [indeterminate]);\n\n  return (\n    <div\n      className={[\"ui-chkc\", className].filter(Boolean).join(\" \")}\n      data-variant={variant === \"card\" ? \"card\" : \"plain\"}\n      data-invalid={error ? true : undefined}\n      data-disabled={disabled || undefined}\n    >\n      {/* React 19 hoists this into <head> once, no matter how many checkboxes render */}\n      <style href=\"ui-checkbox-clean\" precedence=\"default\">\n        {css}\n      </style>\n      <label className=\"ui-chkc__row\" htmlFor={inputId}>\n        <span className=\"ui-chkc__control\">\n          <input\n            {...rest}\n            ref={(el) => {\n              input.current = el;\n              if (el) el.indeterminate = indeterminate;\n              if (typeof ref === \"function\") ref(el);\n              else if (ref) ref.current = el;\n            }}\n            id={inputId}\n            type=\"checkbox\"\n            className=\"ui-chkc__input\"\n            disabled={disabled}\n            aria-invalid={error ? true : undefined}\n            aria-describedby={[describedBy, description ? descId : null, showsError ? errorId : null].filter(Boolean).join(\" \") || undefined}\n            data-indeterminate={indeterminate || undefined}\n          />\n          <span className=\"ui-chkc__box\" aria-hidden=\"true\">\n            <svg className=\"ui-chkc__mark\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\">\n              <path className=\"ui-chkc__tick\" d=\"M4 8.5 6.75 11.25 12 5\" pathLength={1} />\n              <path className=\"ui-chkc__dash\" d=\"M4.5 8h7\" pathLength={1} />\n            </svg>\n          </span>\n        </span>\n        {(label != null || description != null) && (\n          <span className=\"ui-chkc__text\">\n            {label != null && <span className=\"ui-chkc__label\">{label}</span>}\n            {description != null && (\n              <span className=\"ui-chkc__desc\" id={descId}>\n                {description}\n              </span>\n            )}\n          </span>\n        )}\n      </label>\n      {showsError && (\n        <p className=\"ui-chkc__error\" id={errorId} aria-live=\"polite\">\n          {error}\n        </p>\n      )}\n    </div>\n  );\n}\n\nconst css = /* css */ `\n/* Colors and motion as variables – override them on .ui-chkc (see Theming in the docs) */\n.ui-chkc {\n  --ui-chkc-ink: var(--aui-ink, rgba(0, 0, 0, 0.88));\n  --ui-chkc-subtle: var(--aui-muted, rgba(0, 0, 0, 0.45));\n  --ui-chkc-surface: var(--aui-surface, #ffffff);\n  --ui-chkc-edge: var(--aui-border-strong, rgba(0, 0, 0, 0.18));\n  --ui-chkc-edge-hover: rgba(0, 0, 0, 0.32);\n  --ui-chkc-fill: var(--aui-primary, rgba(0, 0, 0, 0.88));\n  --ui-chkc-on-fill: var(--aui-on-primary, #ffffff);\n  --ui-chkc-card-shadow: 0 0 0 0.5px var(--aui-border, rgba(0, 0, 0, 0.08)), 0 1px 1px -1px rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.05);\n  --ui-chkc-card-shadow-hover: 0 0 0 0.5px rgba(0, 0, 0, 0.08), 0 4px 12px -6px rgba(0, 0, 0, 0.09), 0 1px 2px 0 rgba(0, 0, 0, 0.05);\n  --ui-chkc-card-shadow-on: 0 0 0 0.5px rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.05);\n  --ui-chkc-card-wash: rgba(0, 0, 0, 0.025);\n  --ui-chkc-danger: var(--aui-danger, #c4320a);\n  --ui-chkc-focus: var(--aui-focus, rgba(0, 96, 226, 0.7));\n  --ui-chkc-ease: cubic-bezier(0.22, 1, 0.36, 1);\n\n  display: flex;\n  flex-direction: column;\n  gap: 4px;\n  min-width: 0;\n  font-family: var(--aui-font, \"Inter\", \"InterVariable\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif);\n  font-size: 13px;\n  line-height: 20px;\n  color: var(--ui-chkc-ink);\n  -webkit-font-smoothing: antialiased;\n}\n\n.ui-chkc__row { display: flex; align-items: flex-start; gap: 10px; min-width: 0; cursor: pointer; -webkit-tap-highlight-color: transparent; }\n.ui-chkc[data-disabled] .ui-chkc__row { cursor: not-allowed; opacity: 0.5; }\n\n/* The box sits on the first line of the label, centered on it */\n.ui-chkc__control { position: relative; display: grid; flex: none; place-items: center; width: 16px; height: 20px; }\n/* The real input covers the box (and a bit more, for touch) – invisible, but it takes the clicks and the focus */\n.ui-chkc__input { position: absolute; inset: -6px; width: calc(100% + 12px); height: calc(100% + 12px); margin: 0; opacity: 0; cursor: inherit; }\n.ui-chkc__box {\n  display: grid;\n  place-items: center;\n  width: 16px;\n  height: 16px;\n  box-sizing: border-box;\n  border-radius: 4.5px;\n  background: var(--ui-chkc-surface);\n  box-shadow: inset 0 0 0 1px var(--ui-chkc-edge), 0 1px 1px rgba(0, 0, 0, 0.04);\n  color: var(--ui-chkc-on-fill);\n  pointer-events: none;\n  transition: background-color 160ms ease, box-shadow 160ms ease, transform 160ms var(--ui-chkc-ease);\n}\n.ui-chkc__row:hover .ui-chkc__input:not(:checked):not(:indeterminate):not(:disabled) + .ui-chkc__box { box-shadow: inset 0 0 0 1px var(--ui-chkc-edge-hover), 0 1px 1px rgba(0, 0, 0, 0.04); }\n.ui-chkc__input:active:not(:disabled) + .ui-chkc__box { transform: scale(0.9); }\n.ui-chkc__input:is(:checked, :indeterminate) + .ui-chkc__box { background: var(--ui-chkc-fill); box-shadow: none; }\n.ui-chkc__input:focus-visible + .ui-chkc__box { outline: 2px solid var(--ui-chkc-focus); outline-offset: 2px; }\n.ui-chkc[data-invalid] .ui-chkc__input:not(:checked) + .ui-chkc__box { box-shadow: inset 0 0 0 1px var(--ui-chkc-danger); }\n\n/* The check draws itself; the dash grows from the middle */\n.ui-chkc__mark { display: block; width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }\n.ui-chkc__tick, .ui-chkc__dash { stroke-dasharray: 1; stroke-dashoffset: 1; }\n.ui-chkc__input:checked:not(:indeterminate) + .ui-chkc__box .ui-chkc__tick { stroke-dashoffset: 0; transition: stroke-dashoffset 260ms 40ms var(--ui-chkc-ease); }\n.ui-chkc__input:indeterminate + .ui-chkc__box .ui-chkc__dash { stroke-dashoffset: 0; transition: stroke-dashoffset 200ms var(--ui-chkc-ease); }\n\n.ui-chkc__text { display: grid; min-width: 0; }\n.ui-chkc__label { color: var(--ui-chkc-ink); overflow-wrap: anywhere; }\n.ui-chkc__desc { font-size: 12px; line-height: 16px; color: var(--ui-chkc-subtle); overflow-wrap: anywhere; }\n.ui-chkc__error { margin: 0 0 0 26px; font-size: 12px; line-height: 16px; color: var(--ui-chkc-danger); overflow-wrap: anywhere; animation: ui-chkc-message 260ms var(--ui-chkc-ease); }\n\n/* Card: a bordered row, clickable as a whole – a soft shadow on hover; checked, it takes a faint gray fill (no dark edge) */\n.ui-chkc[data-variant=\"card\"] .ui-chkc__row {\n  padding: 10px 12px;\n  border-radius: 8px;\n  background: var(--ui-chkc-surface);\n  box-shadow: var(--ui-chkc-card-shadow);\n  transition: box-shadow 200ms ease, background-color 200ms ease;\n}\n.ui-chkc[data-variant=\"card\"]:not([data-disabled]) .ui-chkc__row:hover { box-shadow: var(--ui-chkc-card-shadow-hover); }\n.ui-chkc[data-variant=\"card\"] .ui-chkc__row:has(.ui-chkc__input:checked) { background: var(--ui-chkc-card-wash); box-shadow: var(--ui-chkc-card-shadow-on); }\n.ui-chkc[data-variant=\"card\"]:not([data-disabled]) .ui-chkc__row:has(.ui-chkc__input:checked):hover { box-shadow: var(--ui-chkc-card-shadow-hover); }\n.ui-chkc[data-variant=\"card\"] .ui-chkc__label { font-weight: 500; }\n.ui-chkc[data-variant=\"card\"] .ui-chkc__error { margin-left: 2px; }\n\n/* Touch screens: 16px text and a bigger box */\n@media (pointer: coarse) {\n  .ui-chkc { font-size: 15px; line-height: 22px; }\n  .ui-chkc__control { width: 20px; height: 22px; }\n  .ui-chkc__box { width: 20px; height: 20px; border-radius: 5.5px; }\n  .ui-chkc__mark { width: 18px; height: 18px; }\n}\n\n@keyframes ui-chkc-message {\n  from { opacity: 0; transform: translateY(-3px); filter: blur(2px); }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .ui-chkc *, .ui-chkc__row { transition-duration: 1ms !important; animation-duration: 1ms !important; }\n}\n\n/* Dark mode: follows a .dark class or data-theme=\"dark\" on any ancestor, e.g. <html> */\n:where(.dark, [data-theme=\"dark\"]) .ui-chkc {\n  --ui-chkc-ink: var(--aui-ink, rgba(255, 255, 255, 0.9));\n  --ui-chkc-subtle: var(--aui-muted, rgba(255, 255, 255, 0.46));\n  --ui-chkc-surface: var(--aui-surface, #1c1c1e);\n  --ui-chkc-edge: var(--aui-border-strong, rgba(255, 255, 255, 0.22));\n  --ui-chkc-edge-hover: rgba(255, 255, 255, 0.36);\n  --ui-chkc-fill: var(--aui-primary, rgba(255, 255, 255, 0.9));\n  --ui-chkc-on-fill: var(--aui-on-primary, #161618);\n  --ui-chkc-card-shadow: 0 0 0 0.5px var(--aui-border, rgba(255, 255, 255, 0.14)), 0 1px 2px 0 rgba(0, 0, 0, 0.4);\n  --ui-chkc-card-shadow-hover: 0 0 0 0.5px rgba(255, 255, 255, 0.16), 0 6px 14px -6px rgba(0, 0, 0, 0.45);\n  --ui-chkc-card-shadow-on: 0 0 0 0.5px rgba(255, 255, 255, 0.18), 0 1px 2px 0 rgba(0, 0, 0, 0.4);\n  --ui-chkc-card-wash: rgba(255, 255, 255, 0.045);\n  --ui-chkc-danger: var(--aui-danger, #ff8a7a);\n  --ui-chkc-focus: var(--aui-focus, rgba(77, 141, 255, 0.8));\n}\n`;\n"}]}