- Components
Checkbox
A box that is on, off or mixed, with its label inside a 44 px row. Built on Radix Checkbox.
npx shadcn add @klarhimmel/checkboxWhen to use
Use for
Choices that are submitted with a form, and lists where several can be picked.
Not for
A setting that applies right away. Use Switch.
Pairs with
Field rows, fieldset and legend, Button.
Preview
Anyone in your organisation can see it.
You need to accept the terms to continue.
checkbox.tsx
import { Theme } from '@/components/klarhimmel/theme/style';
import { Checkbox } from '@/components/klarhimmel/ui/checkbox';
export default function Example() {
return (
<Theme theme="brand">
<Checkbox label="Send me the weekly summary" defaultChecked />
</Theme>
);
}Install
Import
import { Checkbox } from '@/components/klarhimmel/ui/checkbox';- Lands in
- components/klarhimmel/ui/checkbox.tsx, plus cn() and the theme item if they are missing. Import theme.css after Tailwind.
- Render in
- Render it inside <Theme>, once near the root of your app. Tokens and fonts are scoped to it, so outside it the component is unstyled.
Examples
Group
import { useState } from 'react'; import { Checkbox } from '@/components/klarhimmel/ui/checkbox'; const rooms = ['Seaside suite', 'Garden room', 'Attic double']; export default function CheckboxGroup() { const [picked, setPicked] = useState<string[]>(['Garden room']); const all = picked.length === rooms.length ? true : picked.length ? 'indeterminate' : false; return ( <fieldset className="flex flex-col"> <legend className="mb-1 text-kh-small font-medium">Rooms to include</legend> <Checkbox label="All rooms" checked={all} onCheckedChange={(c) => setPicked(c === true ? rooms : [])} /> <div className="flex flex-col pl-8"> {rooms.map((r) => ( <Checkbox key={r} label={r} checked={picked.includes(r)} onCheckedChange={(c) => setPicked((p) => (c === true ? [...p, r] : p.filter((x) => x !== r)))} /> ))} </div> <p className="mt-2 text-kh-small text-kh-muted" aria-live="polite">{picked.length} of {rooms.length} selected</p> </fieldset> ); }Invalid
import { useState, type FormEvent } from 'react'; import { Button } from '@/components/klarhimmel/ui/button'; import { Checkbox } from '@/components/klarhimmel/ui/checkbox'; export default function CheckboxInvalid() { const [agreed, setAgreed] = useState(false); const [tried, setTried] = useState(false); const [sent, setSent] = useState(false); const invalid = tried && !agreed; const submit = (e: FormEvent) => { e.preventDefault(); setTried(true); setSent(agreed); }; return ( <form onSubmit={submit} className="flex max-w-md flex-col items-start gap-3"> <Checkbox label="I accept the booking terms" invalid={invalid} hint={invalid ? 'You need to accept the terms to continue.' : undefined} checked={agreed} onCheckedChange={(c) => setAgreed(c === true)} /> <Button type="submit">Book the room</Button> <p role="status" className="text-kh-small text-kh-muted">{sent ? 'Booked. A confirmation is on its way.' : ''}</p> </form> ); }
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
label | ReactNode | - | The visible label. Without one, pass aria-label. |
hint | ReactNode | - | Help text under the label, wired as the description. |
invalid | boolean | false | Marks the box invalid; the hint turns into the error text. |
checked / defaultChecked | boolean | 'indeterminate' | - | Controlled or uncontrolled state. "indeterminate" shows a dash. |
onCheckedChange | (checked: boolean | 'indeterminate') => void | - | Called when the state changes. |
disabled | boolean | false | Prevents interaction. |
className | string | - | Extra classes, merged after the component's own. |
Accessibility
| Key | Does |
|---|---|
Space | Checks or unchecks |
Tab | Moves focus; the ring is always visible |
A group needs a fieldset and legend so the question is read with each box.
Accessibility