Checkbox
Use a checkbox for an option that stands on its own: a setting the user turns on
or off, or one of several answers they can pick at the same time. If the choices
are mutually exclusive, use radios; if the change applies the moment it is
flipped rather than when a form is submitted, a Switch reads better.
Checkbox renders a <span> with role="checkbox" and a hidden <input> next
to it. That means it carries no accessible name of its own — the
Field.Label wrapped around it above is what gives it one,
and every example on this page keeps that pattern. A Field generates the id,
points the label at the control and carries the name into form submission, so
none of that appears in the demo code.
Import
import { Checkbox } from "@forte-ui/react";Examples
Sizes
size sets the painted box — 0.875rem, 1rem or 1.25rem. The mark scales
with the box, and its stroke width is retuned at each size so the tick keeps
the same optical weight rather than turning heavy on the small box and spindly
on the large one. The clickable area does not shrink with the box: a
pseudo-element on the root holds the hit target at 24×24px minimum
(WCAG SC 2.5.8), so a sm checkbox is no harder to hit than an lg one.
Indeterminate
indeterminate puts the checkbox in a mixed state — neither ticked nor
unticked. The mark becomes a dash instead of a tick, aria-checked becomes
"mixed", and data-indeterminate appears on both the root and the indicator,
so either can be targeted from CSS (data-[indeterminate]:… in Tailwind).
It is a summary rather than a third value the user can choose: no activation ever produces a mixed box, so it is always derived from something else — most often a list that is only partly selected.
indeterminate and checked are independent. A checkbox can be both, and when
it is, the mixed state wins visually (the tick retracts, the dash draws in)
while the hidden input still submits whatever checked says.
Groups
CheckboxGroup collects a series of checkboxes into one array of values, names
them together and can disable them together. It has a page of its own:
CheckboxGroup.
Disabled
disabled dims the box, blocks pointer and keyboard interaction, and sets
aria-disabled. On its own it reaches the control and not the words next to it —
so put it on the Field.Root instead, as the demo does. That takes precedence
over the control's own prop and puts data-disabled on the label too, which is
what keeps the reason an option is unavailable from sitting at full contrast
beside a greyed-out box.
Accessibility
| Key | Behaviour |
|---|---|
| Tab | Moves focus to the checkbox. Each checkbox is its own tab stop, including inside a CheckboxGroup. |
| Space | Toggles the focused checkbox. On a parent checkbox it drives the whole group — all, none, then back to the earlier partial selection. |
| Enter | Does not toggle. Inside a form it submits the form, matching a native checkbox. |
The root exposes role="checkbox" and aria-checked — true, false, or
"mixed" while indeterminate. The <input> beside it is aria-hidden and
exists to carry the value into form submission, so name, value, required
and form behave as they would on a native checkbox.
Motion. The mark is drawn on rather than faded in: both paths carry
pathLength="1", and the stylesheet animates stroke-dashoffset from 1 to
0 over --forte-checkbox-draw-duration. That choice matters for reduced
motion. The tick is information — for anyone who cannot rely on the fill colour
it is the only thing separating on from off — so it must never be animated with
a travel or scale token that collapses to zero. Under
prefers-reduced-motion: reduce the library only shortens the durations
(--forte-duration-normal drops from 240ms to 120ms), so the mark still appears;
it just draws faster.
Under forced colours, the box paints with Canvas/CanvasText, a checked or
mixed box with Highlight, and a disabled one with GrayText on Canvas —
the tick stays visible in every state.
Theming
Every visual decision is a custom property declared on the checkbox root. Set
them on the checkbox itself — through className or an inline style — rather
than on an ancestor: the component declares its own defaults on the root
element, and an element's own declaration beats an inherited value.
| Property | Controls | Default |
|---|---|---|
--forte-checkbox-size | Width and height of the painted box. size="sm" sets 0.875rem, size="lg" sets 1.25rem. | 1rem |
--forte-checkbox-radius | Corner radius of the box. | var(--forte-radius-2) |
--forte-checkbox-border-width | Border width. | 1px |
--forte-checkbox-mark-width | Stroke width of the tick and dash. Unitless on purpose — the value is in SVG user units inside a 16-unit viewBox, so the stroke scales with the box instead of thinning out at larger sizes. size retunes it: 2.25 for sm, 1.75 for lg. | 2 |
--forte-checkbox-duration | Colour transitions on the box and the fade of the indicator. | var(--forte-duration-fast) |
--forte-checkbox-draw-duration | How long the mark takes to draw in and out. | var(--forte-duration-normal) |
--forte-checkbox-bg | Fill while unchecked. | var(--forte-color-background) |
--forte-checkbox-bg-hover | Fill on hover while unchecked. | var(--forte-color-panel-hover) |
--forte-checkbox-border-color | Border colour while unchecked. | var(--forte-color-border) |
--forte-checkbox-border-color-hover | Border colour on hover while unchecked. | var(--forte-color-border-strong) |
--forte-checkbox-fill | Fill while checked or indeterminate. | var(--forte-color-primary) |
--forte-checkbox-fill-hover | Fill on hover while checked or indeterminate. | var(--forte-color-primary-hover) |
--forte-checkbox-mark-color | Colour of the tick and dash — it becomes the root's color, and the marks stroke with currentColor. | var(--forte-color-on-primary) |
The three -hover values only ever apply on devices that report hover support,
so a touch device never sticks in a hover colour after a tap.
tone reassigns --forte-checkbox-fill, --forte-checkbox-fill-hover and
--forte-checkbox-mark-color. Inside a Field.Root, an invalid field overrides
those and the border colours with the danger palette, so a validation failure
never renders in a brand colour.
API reference
Checkbox
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | undefined | Whether the checkbox is currently ticked. Pairs with `onCheckedChange` for a controlled checkbox; use `defaultChecked` for an uncontrolled one. |
className | string | Additional class name(s). Applied after the internal styles so consumer utilities (e.g. Tailwind) win without needing `!important`. | |
defaultChecked | boolean | false | Whether the checkbox starts ticked when uncontrolled. |
indeterminate | boolean | false | Mixed state: neither ticked nor unticked. Renders a dash instead of a tick and sets `data-indeterminate` on both the root and the indicator. Independent of `checked` — a checkbox that is indeterminate submits whatever `checked` says it submits. |
indicatorClassName | string | Additional class name(s) for the indicator — the element that holds the tick. Applied after the internal styles. | |
nativeButton | boolean | false | Set to `true` when `render` replaces the root with a real `<button>`. The root is a `<span>` by default so that an enclosing `<label>` stays valid HTML; for the sibling-label pattern (`<label htmlFor>` + `id`) pass `nativeButton render={<button />}` instead. With `nativeButton`, `id` lands on the root element rather than on the hidden input. |
parent | boolean | false | Marks this checkbox as the one that controls every other checkbox in the group. Only works inside a `CheckboxGroup` that has been given `value` and `allValues`; outside one it does nothing at all. The group drives the indeterminate state when only some children are ticked. |
size | CheckboxSize | md | Size of the box. The tick scales with it, so the mark keeps its optical weight at every size. |
tone | CheckboxTone | primary | Which semantic colour set the checked fill draws from. Inside a `Field.Root` an invalid field overrides this with the danger palette, so a validation error always reads as an error. |
Anything Base UI's Checkbox.Root accepts passes through as well — including
name, value, uncheckedValue, onCheckedChange, disabled, readOnly,
required, form, inputRef and render.
CheckboxGroup
See CheckboxGroup for the group's own prop
table, its theming property and the parent-checkbox rules.