Checkbox
Description
Section titled “Description”.bp-checkbox wraps a native <input type="checkbox"> with its label in an inline-flex container. Three companion classes extend the pattern: .bp-checkbox-field adds an error message slot, .bp-checkbox-group arranges multiple checkboxes in a <fieldset>, and .bp-checkbox-group--inline switches to a horizontal layout. No JavaScript required for checked or disabled states; indeterminate requires one JS property set.
Default (unchecked)
<label class="bp-checkbox"><input class="bp-checkbox__input" type="checkbox" />Subscribe to newsletter</label>Checked
<label class="bp-checkbox"><input class="bp-checkbox__input" type="checkbox" checked />I agree to the terms</label>Indeterminate
<label class="bp-checkbox"><input class="bp-checkbox__input demo-checkbox-indeterminate" type="checkbox" />Select all</label><script>document.querySelector('.demo-checkbox-indeterminate').indeterminate = true</script>Disabled
<label class="bp-checkbox"><input class="bp-checkbox__input" type="checkbox" disabled />This option is unavailable</label><label class="bp-checkbox"><input class="bp-checkbox__input" type="checkbox" checked disabled />Pre-selected (read-only)</label>Error state
You must accept the terms to continue.
<div class="bp-checkbox-field"><label class="bp-checkbox"> <input class="bp-checkbox__input" type="checkbox" aria-invalid="true" aria-describedby="terms-error" /> I accept the terms and conditions</label><p class="bp-checkbox-field__error" id="terms-error" role="alert"> You must accept the terms to continue.</p></div>Group — stacked
<fieldset class="bp-checkbox-group"><legend class="bp-checkbox-group__legend">Notification preferences</legend><label class="bp-checkbox"> <input class="bp-checkbox__input" type="checkbox" name="notify" value="email" checked /> Email</label><label class="bp-checkbox"> <input class="bp-checkbox__input" type="checkbox" name="notify" value="sms" /> SMS</label><label class="bp-checkbox"> <input class="bp-checkbox__input" type="checkbox" name="notify" value="push" /> Push notification</label></fieldset>Group — inline
<fieldset class="bp-checkbox-group bp-checkbox-group--inline"><legend class="bp-checkbox-group__legend">Dietary preferences</legend><label class="bp-checkbox"> <input class="bp-checkbox__input" type="checkbox" name="diet" value="vegetarian" /> Vegetarian</label><label class="bp-checkbox"> <input class="bp-checkbox__input" type="checkbox" name="diet" value="vegan" checked /> Vegan</label><label class="bp-checkbox"> <input class="bp-checkbox__input" type="checkbox" name="diet" value="gluten-free" /> Gluten-free</label></fieldset>Public API
Section titled “Public API”| Variable | Default | Description |
|---|---|---|
--checkbox-size | 1.125rem | Width and height of the control box |
--checkbox-radius | var(--bp-radius-sm) | Border radius of the control box |
--checkbox-border | 1px solid var(--bp-color-border) | Border in unchecked state |
--checkbox-bg | var(--bp-color-bg-elevated) | Background in unchecked state |
--checkbox-color | var(--bp-primary) | Fill color when checked or indeterminate |
--checkbox-check-color | #fff | Checkmark and dash icon color |
--checkbox-gap | var(--bp-space-2) | Gap between control box and label text |
--checkbox-label-color | var(--bp-color-text) | Label text color |
--checkbox-error-color | var(--bp-color-error) | Border and text color in error state |
--checkbox-group-gap | var(--bp-space-3) | Gap between items in a stacked group |
--checkbox-group-inline-gap | var(--bp-space-4) | Gap between items in an inline group |
Customization
Section titled “Customization”Custom brand color and larger control
<style>.demo-checkbox--brand { --checkbox-color: #7c3aed; --checkbox-size: 1.375rem; --checkbox-radius: var(--bp-radius-md);}</style><label class="bp-checkbox demo-checkbox--brand"><input class="bp-checkbox__input" type="checkbox" checked />Enable feature flag</label>Accessibility
Section titled “Accessibility”- Label wrapping — placing
<input>inside<label>creates an implicit association. Nofor/idpairing is needed. Clicking anywhere on the label text toggles the checkbox. - Error state — add
aria-invalid="true"on the<input>to signal an invalid state to assistive technology, andaria-describedbypointing to the error paragraph’sid. The error paragraph should carryrole="alert"so screen readers announce the message immediately when it appears. - Groups — wrap related checkboxes in a
<fieldset>with a<legend>. Screen readers announce the legend text before each option, giving full context (e.g. “Notification preferences, Email, checkbox, unchecked”). - Indeterminate —
indeterminateis a JavaScript property, not an HTML attribute. It cannot be set viaindeterminate=""in markup; you must set it viaelement.indeterminate = true. Screen readers announce this state as “mixed” to users. - Disabled — the native
:disabledpseudo-class communicates unavailability. Do not simulate disabled state witharia-disabledon a label; mark the<input>itself asdisabled. - Focus —
:focus-visibleprovides a visible ring styled viavar(--bp-focus-ring). Do not suppress:focus-visibleon checkboxes.
Browser APIs
Section titled “Browser APIs”| API | Availability | Used for | Without it | Polyfill |
|---|---|---|---|---|
appearance: none | Widely available Baseline 2023 | Removing OS default checkbox chrome | Default OS checkbox style shown | None |
:focus-visible | Widely available Baseline 2022 | Keyboard-only focus ring | Focus ring always visible (:focus) | None |
:has() | Widely available Baseline 2023 | Disabling opacity on wrapper label | Wrapper label stays full opacity | None |
indeterminate (JS prop) | Widely available Baseline 2015 | Indeterminate visual state | State not settable | None |
Internals
Section titled “Internals”--_size,--_radius,--_border,--_bg,--_color,--_check-color,--_gap,--_label-color— component-private aliases resolved on.bp-checkbox. Do not set these directly.- The checkmark and dash icons are inline SVG data URIs embedded in
background-image— no external assets needed. :checkedand:indeterminatestates are CSS-native; no JS class toggling occurs..bp-checkbox:has(.bp-checkbox__input:disabled)propagates thecursor: not-allowedandopacity: 0.5to the full label wrapper via the:has()selector.