Checkbox
A Checkbox lets users toggle a boolean state on or off. It pairs a visually hidden native <input type="checkbox"> with a custom styled box, giving full keyboard and form support while keeping the Win95 bevel aesthetic.
Overview
When to use
- To let users toggle a single boolean option (paid / unpaid, enabled / disabled).
- Inside a list to mark individual items.
- In forms where an option can be independently on or off.
When not to use
- To select one option from a group — use a radio button instead.
- As a trigger for an immediate action — use a Button.
States
Unchecked — default state, empty box with sunken bevel.
Checked — white background with pixel-art checkmark rendered inside.
Disabled unchecked — gray background (--color-accent-primary) with not-allowed cursor.
Disabled checked — gray background with muted checkmark color.
Accessibility
The component uses a native <input type="checkbox"> (visually hidden, positioned over the visual box) to handle:
- Keyboard: Space bar toggles; Tab moves focus naturally.
- Screen readers: The element is announced as a checkbox with its current state.
- Focus ring: Visible
outlineon keyboard focus viafocus-visible.
Always associate a visible label using id + <label htmlFor>:
<Checkbox id="paid" checked={isPaid} onCheckedChange={setIsPaid} />
<label htmlFor="paid">Mark as paid</label>
Usage inside a label
The recommended pattern for toggling a row or list item is to wrap the checkbox and its associated content in a <label>. Clicking anywhere on the label — including the text — toggles the checkbox:
<label htmlFor="paid" style={{ display: 'flex', gap: '8px', cursor: 'pointer' }}>
<Checkbox id="paid" checked={isPaid} onCheckedChange={setIsPaid} />
Bill name
</label>
Controls
Interactive controls are available in Storybook. Open Storybook
Props
Token mapping
| Property | Token |
|---|---|
| background (unchecked) | --color-bg-page |
| background (checked) | --color-bg-page |
| background (disabled) | --color-accent-primary |
| checkmark color | --primitive-black |
| checkmark color (disabled) | --color-text-muted |
| bevel (top-left outer) | --sunken-outer-dark |
| bevel (bottom-right outer) | --sunken-outer-light |
| bevel (top-left inner) | --sunken-inner-dark |
| bevel (bottom-right inner) | --sunken-inner-light |
Changelog
| Date | Change |
|---|---|
| 2026-03-23 | Migrated to Tailwind; replaced div+role with native input for keyboard/form support; API updated to onCheckedChange; added disabled state; checkmark uses currentColor; added index.ts, stories, and documentation |
| 2026-03-15 | Initial component created |