Forms
Checkbox
Accessible checkbox built on react-aria-components, styled with the quebi design system. Supports selected, indeterminate, invalid, and disabled states, plus grouping.
forminputbooleaninteractive
States
Default, selected, indeterminate, invalid, and disabled.
Group
Several related checkboxes with CheckboxGroup.
Controlled
Source
Copy this into your project. Resolve its dependencies from the registryDependencies in the component's API entry.
"use client"
import { Minus } from "lucide-react"
import {
CheckboxGroup as CheckboxGroupPrimitive,
type CheckboxGroupProps,
Checkbox as CheckboxPrimitive,
type CheckboxProps,
composeRenderProps,
} from "react-aria-components"
import { cn } from "@/lib/utils"
/**
* Checkbox — quebi design system
*
* Built on react-aria-components. An 18px square with a cyan-tinted border;
* selected state fills with brand teal and shows a chunky check glyph. Focus
* uses the quebi teal ring; invalid uses red.
*/
export function CheckboxGroup({ className, ...props }: CheckboxGroupProps) {
return (
<CheckboxGroupPrimitive
{...props}
data-slot="control"
className={cn("flex flex-col gap-3", className)}
/>
)
}
export function Checkbox({ className, children, ...props }: CheckboxProps) {
return (
<CheckboxPrimitive
data-slot="control"
className={composeRenderProps(className, (className) =>
cn("group flex items-center gap-3 disabled:opacity-50 disabled:cursor-not-allowed", className),
)}
{...props}
>
{composeRenderProps(children, (children, { isSelected, isIndeterminate, isInvalid }) => (
<>
<span
data-slot="indicator"
className={cn(
"relative flex size-[18px] shrink-0 items-center justify-center rounded-quebi-sm border bg-transparent",
"transition-colors duration-150",
"border-cyan-500/30",
"group-data-[selected]:border-quebi-brand group-data-[selected]:bg-quebi-brand",
"group-data-[indeterminate]:border-quebi-brand group-data-[indeterminate]:bg-quebi-brand",
"group-data-[focus-visible]:ring-2 group-data-[focus-visible]:ring-quebi-brand/50 group-data-[focus-visible]:ring-offset-2 group-data-[focus-visible]:ring-offset-quebi-bg",
isInvalid &&
"border-red-500 group-data-[focus-visible]:ring-red-500/50 group-data-[selected]:border-red-500 group-data-[selected]:bg-red-500",
)}
>
{isIndeterminate ? (
<Minus className="size-3 text-quebi-bg" strokeWidth={3} aria-hidden="true" />
) : isSelected ? (
<span
aria-hidden="true"
className="block h-[9px] w-[5px] -translate-y-px rotate-45 border-quebi-bg border-r-2 border-b-2"
/>
) : null}
</span>
{children != null && (
<span data-slot="label" className="text-sm text-white select-none">
{children}
</span>
)}
</>
))}
</CheckboxPrimitive>
)
}