Checkbox
Purpose
A Checkbox is a binary toggle control that allows a user to independently enable or disable an option. Use it for settings that can be individually switched on or off, for selecting one or more items from a list (multi-selection), or for accepting required conditions (e.g., terms of service). Do NOT use a Checkbox for a single binary on/off setting that takes immediate effect — use a Toggle Switch instead. Do NOT use a Checkbox as a radio button substitute (selecting only one item from a group) — use a Radio Group for mutually exclusive choices.
Anatomy
A Checkbox is composed of:
- Checkbox control (required): the square interactive element that shows the checked, unchecked, or indeterminate visual state. 16x16px by default.
- Check indicator (inside the control): a checkmark icon rendered inside the control when checked. An indeterminate indicator (a horizontal dash or a filled square) when in indeterminate state.
- Label (strongly recommended): text immediately to the right of the checkbox control describing the option. A checkbox without a visible label MUST have an
aria-labeloraria-labelledby. - Description (optional, within Field): secondary helper text below the label clarifying what the option means or its implications. Rendered via FieldDescription in
--force-color-text-tertiary. - Field Error (optional, within Field): a validation error for the checkbox or the checkbox group. Rendered via FieldError in
--force-color-text-error.
In a Checkbox Group (multiple related checkboxes), the group uses a FieldSet with a FieldLegend as the group's visible heading.
Variants
Checkboxes do not have visual style variants in the Force UI system. All checkboxes use the same visual treatment regardless of context. Size (currently 16x16px) is fixed. The only configurational differences are:
- Standalone checkbox: a single checkbox with its own Field wrapping for an isolated binary setting.
- Checkbox group: multiple checkboxes inside a FieldSet, sharing a FieldLegend as their common label. Each checkbox has its own individual label.
- Indeterminate state: used in "select all" header checkboxes in data tables, when some (but not all) child rows are selected.
States
Unchecked (default): The control shows an empty square. Border is --force-color-border-default (neutral-200, at its accessible stroke weight). Background is --force-color-bg-surface.
Unchecked hover: Border darkens to --force-color-border-strong. Background transitions to --force-color-bg-interactive-hover (neutral-50). Transition at --force-duration-fast (150ms).
Unchecked focus (keyboard): Border changes to --force-color-border-focus (indigo-500). Focus ring --force-shadow-focus-ring (0 0 0 3px indigo-100) applied. Visible at all times — do not suppress focus styles.
Checked: Background fills to --force-color-bg-primary (indigo-500). Border matches (--force-color-border-primary). The check icon renders in --force-color-text-on-primary (white). The check icon is a standard checkmark from Material Symbols Rounded.
Checked hover: Background darkens to --force-color-bg-primary-hover (indigo-600).
Checked focus: Same focus ring as unchecked focus, applied over the checked background.
Indeterminate: Background fills to --force-color-bg-primary. A horizontal dash or partially-filled indicator renders in --force-color-text-on-primary. Visually distinct from checked. Set via data-state="indeterminate" on the control element.
Disabled (unchecked): Opacity --force-opacity-disabled (0.5) applied to the control and label. Cursor not-allowed. Background is --force-color-bg-muted. The native disabled attribute must be set.
Disabled (checked): Same disabled treatment, with the filled checked background at reduced opacity.
Error: When a checkbox or group has a validation error (e.g., "You must accept the terms"), the checkbox control's border changes to --force-color-border-error. The FieldError message renders below. When focused in error state, --force-shadow-focus-ring-error is applied. aria-invalid="true" is set on the control.
Behavior
- Toggle: Clicking the checkbox control or its associated label text toggles the checked state. The click target for the label text also activates the checkbox — this is standard native behavior for
<label>associated viahtmlFor. - Touch target: The minimum touch target is 24x24px for the control itself. The label text also serves as a tap target, making the effective touch target much larger. Ensure at least 8px of space between adjacent checkboxes on touch surfaces.
- Keyboard: Space toggles the checkbox when it is focused. Tab moves focus to the next focusable element (checkboxes within a group are NOT automatically navigated with arrow keys — Tab moves through each checkbox individually). This distinguishes checkboxes from radio buttons.
- Indeterminate state: Indeterminate is a programmatic state — it cannot be set by the user directly. It is set by the application when a "select all" checkbox has some (but not all) child items selected. Clicking an indeterminate checkbox should move it to checked (selecting all children). Clicking again moves it to unchecked (deselecting all children).
- Form submission: Checkboxes with the native
checkedstate submit theirvalueto a form. When unchecked, no value is submitted. For controlled form patterns, manage state through the form library's state rather than relying on native form submission.
Accessibility
- Each checkbox MUST have an associated label. Use
<label htmlFor="...">or wrap the input inside a<label>. Never use an icon or color alone as the label. - For checkbox groups, wrap all checkboxes in a
<fieldset>with a<legend>that names the group. This ensures screen readers announce "Group name: Option label, checkbox, unchecked" for each item. - Set
aria-required="true"on a required standalone checkbox (e.g., terms acceptance). For required groups, set it on the fieldset or announce via the legend. - Set
aria-invalid="true"on any checkbox with a validation error. Associate the error message viaaria-describedby. - The indeterminate state MUST be communicated to assistive technology. Use the
aria-checked="mixed"attribute on the checkbox element when it is in the indeterminate state. - Do NOT use color alone to convey the checked state. The check icon inside the control provides shape-based differentiation in addition to the fill color.
- When a "select all" checkbox is present in a table header, ensure it has an explicit
aria-label="Select all rows"(or similar) since it has no adjacent text label in the column header.
Composition
- A standalone Checkbox is typically wrapped in a Field (with FieldLabel and optional FieldDescription).
- Multiple related checkboxes form a Checkbox Group inside a FieldSet (with FieldLegend).
- In a data table, a Checkbox appears in the leftmost column header (select all, indeterminate state) and each body row (select individual row). These checkboxes are tightly coupled to the table's row selection state.
- A Checkbox may appear in a Dropdown Menu item (e.g., "Show column", "Enable option") — in this context, the entire menu item row is the click target, and the checkbox is visual-only (the
role="menuitemcheckbox"is on the menu item, not a standalone checkbox element). - Do NOT combine a Checkbox with a Toggle Switch in the same group — they imply different interaction models (immediate effect vs. form submission).
- Do NOT use a Checkbox inside a Button — if you need a pressed/active toggle inside a button shape, use a Toggle Button.
Guidance
Choose Checkbox over other controls:
- Use Checkbox (not Toggle Switch) when the setting takes effect only on form submission, not immediately.
- Use Checkbox (not Radio Group) when the user can select zero, one, or more options independently.
- Use Checkbox (not Multi-Select) when there are five or fewer options to choose from and they benefit from always being visible in the UI.
Label writing:
- Write checkbox labels as positive statements that describe what happens when the checkbox is checked: "Enable notifications" not "Disable notifications." Negative labels ("Do not send me emails") are confusing when combined with the binary checked/unchecked visual.
- Be specific: "Send email notifications for critical alerts" is clearer than "Notifications."
Implementation requirement — explicit visual properties:
The checkbox control MUST have explicit background, border, and indicator color declarations using Force UI tokens for every state (unchecked, checked, indeterminate, hover, focus, disabled, error). Set appearance: none on the native <input type="checkbox"> to strip browser chrome, then apply all visual styling through tokens. Without this, the checkbox will render with browser-native dark-mode styling when the host page uses a dark color scheme. See design principle P8.
Common mistakes to avoid:
- Do NOT leave
backgroundorborderunset on the checkbox control — it will inherit browser/host dark-mode defaults. - Do NOT use a Checkbox to confirm a non-binary, multi-step decision — that is a Dialog's job.
- Do NOT pre-check a checkbox that the user has not explicitly chosen, unless the system can genuinely determine the correct default and the pre-selection is clearly beneficial (e.g., "Remember me" for trusted devices).
- Do NOT place more than eight checkboxes in a group without considering a Multi-Select — groups beyond eight become unwieldy to scan.
- Do NOT style checkboxes to look like radio buttons or vice versa — the shape difference (square vs. circle) is a semantic affordance.
Token Usage
Control (unchecked)
- Background:
--force-color-bg-surface - Border:
1px solid --force-color-border-default - Border radius: 4px (fixed, smaller than
--force-radius-buttonto preserve the square-ish affordance)
Control (unchecked hover)
- Background:
--force-color-bg-interactive-hover - Border:
--force-color-border-strong
Control (checked)
- Background:
--force-color-bg-primary - Border:
--force-color-border-primary - Check icon color:
--force-color-text-on-primary
Control (checked hover)
- Background:
--force-color-bg-primary-hover
Control (indeterminate)
- Background:
--force-color-bg-primary - Border:
--force-color-border-primary - Indicator color:
--force-color-text-on-primary
Control focus ring
- Border:
--force-color-border-focus - Focus ring:
--force-shadow-focus-ring
Control error
- Border:
--force-color-border-error - Error focus ring:
--force-shadow-focus-ring-error
Disabled
- Opacity (control + label):
--force-opacity-disabled(0.5) - Background:
--force-color-bg-muted
Label text
- Color:
--force-color-text-primary - Font size:
--force-font-size-sm(14px) - Font weight:
--force-font-weight-medium(500)
Description text
- Color:
--force-color-text-tertiary - Font size:
--force-font-size-xs(12px)
Error message
- Color:
--force-color-text-error - Font size:
--force-font-size-xs(12px)
Transition
- Duration:
--force-duration-fast(150ms) - Easing:
--force-easing-standard