Skip to content
bitboss-ui3.0.0-beta.31

BbCheckbox

One checkbox for a yes/no that stands on its own, with the full field surface.

import { BbCheckbox } from 'bitboss-ui';

On this page

Use it for

Reach for BbCheckbox when the answer is one yes/no standing on its own: accept the terms, remember this device, include archived records.

Use something else when

  • BbCheckboxGroup: several boxes inside one set. Never hand-stack BbCheckbox
  • BbRadioGroup: exactly one choice out of the set
  • BbSelect: the set is long, searchable or fetched
  • BbSwitch: it is the state of a thing, like notifications or dark mode

Pass Through

Hover or tap a part to outline it. Toggles flip loading, errors and warnings when the component has them — only parts highlight.

Guidance while the control is focused.

A checkbox is an answer, a switch is a state. A Save button below the field changes nothing about which of the two you have.

This page also covers the states, the value and the layout of BbRadio and BbSwitch. They behave identically, and those pages link back here.

Default

You need a label and a bound v-model. Clicking the box or the label toggles the value.

Model value: false

label is required: it is the control's accessible name. id and name are generated when you leave them off, but pass both in a form that posts.

States and messages

A checkbox carries the same chrome as every other field in the library, and the four message channels are not interchangeable.

You can leave the programme at any time.
Accept the terms to continue.
Exports will take noticeably longer.

Put durable information in description and momentary guidance in hint. hint shows only while the field has focus, unless persistent-hint pins it open.

errors takes a string or an array, and a non-empty value puts the field into the error state on its own. has-errors gives you the styling without a message. warnings is the amber counterpart and does not set aria-invalid. When both are set errors win, so the field is never red and amber at once.

All four are wired into aria-describedby in that order: errors, warnings, description, hint. An aria-live="polite" region announces them when they appear and re-reads them on focus, so keep them short.

readonly is not disabled

disabled sets the native attribute: the box leaves the tab order and its value is never submitted. readonly keeps it focusable and submitted, and blocks only the changes. Clicks and Space do nothing, and update:modelValue never fires.

Use readonly for a value that must reach the server without changing, and disabled for a value that must not post. When you disable, say why in the description: a box that is off for no visible reason reads as a bug.

What the model holds

With nothing else set the model is true or false. Point true-value and false-value at your own values and it holds those.

Posted as digest=off whichever way it is set.

Any serializable value works, matched structurally rather than by reference. update:modelValue emits true-value on check and false-value on uncheck, so never read the checked state off a @click or @change handler.

For a classic form post the native input carries name and a serialized value. An unchecked box posts nothing, which is standard HTML and a recurring surprise on the server. submit-when-false renders a hidden input carrying false-value, so the field always arrives.

In an SPA or an Inertia app you bind into form state and drop both name and submit-when-false.

vue
<BbCheckbox v-model="form.newsletter" label="Subscribe to the newsletter" />

The "I agree" box, with links inside its label, that unlocks the primary action.

The label slot decorates the text without touching the accessible name, and receives { text, hasErrors, hasWarnings }. The whole control is one <label>, so the links inside it have to be real anchors. A click on an <a> navigates, a click on anything else toggles the box.

Bind the button's disabled to the model, rather than intercepting the click and reading the box state by hand.

required sets the native attribute and puts the box into the browser's own validation. For app-driven validation use errors: you control the wording, the timing and the announcement.

Select all

A parent box that summarises a group is checked when every child is on, and indeterminate when only some are.

Teams to notify

2 of 7 teams notified

indeterminate is a purely visual third state, a dash instead of a check. It never enters the model, and the component re-applies it after every click. If your server needs a real tri-state value, model it separately.

The master box is driven from outside, so it takes checked rather than a v-model. Passing both gives it two sources of truth: pick one.

The children are a BbCheckboxGroup, not a hand-stacked row.

Coming from v2

checked used to accept the strings 'true' and 'false' too. In v3 it is a plain boolean, so checked="false" no longer compiles. That string was truthy and checked the box, so this is a loud failure replacing a silent one.

Layout

By default the box comes first and the label follows. reverse swaps them, and that is what turns a checkbox into a settings row.

direction decides how the two halves share the width. auto, the default, sizes each to its content on one line. horizontal splits them evenly and vertical stacks them. A two-token pattern like "x xxxxxxx" splits them by token length, the first token sizing the element that renders first. input-position and label-position (left, center, right) then align the box and the text inside their columns.

hide-label keeps the label for screen readers and hides it on screen. It is for a table's selection column, where visible text already labels the box, and nothing else.

Coming from v2

reverse means the opposite of what it used to, and the compiler cannot see it. The default layout is unchanged, so a checkbox with no reverse is fine. What changed is the explicit value. In v2 reverse defaulted to true and :reverse="false" produced the swap. In v3 reverse defaults to false, and passing it opts into the swap. Delete reverse and :reverse="true", rewrite :reverse="false" as a bare reverse, and grep for the prop on all three single controls.

Custom markup

Sometimes the design is not a labelled row at all: a grid of selectable cards, say. BbBaseCheckboxIcon gives you the library's box, and you own everything around it.

Teams to notify

BbBaseCheckboxIcon draws and nothing more: the rounded box, the animated check and the indeterminate dash, with no input, no events and no state. You tell it how to look with checked, indeterminate, disabled, readonly, has-errors, has-warning and focus-visible.

Because it is aria-hidden and cannot be focused, it has to sit on a real control. Copy the shape in the demo: a <label> wrapping an <input type="checkbox"> hidden with sr-only. Never display: none, which takes the input out of the tab order and out of the form. Keyboard, focus and form submission come free. Pass focus-visible yourself, or a keyboard user gets no ring.

There is a smaller step before that one. The icon slot on BbCheckbox replaces only the box visual and keeps the input, the label and the field chrome. It receives the live state: checked, indeterminate, disabled, readonly, hasErrors, focused, focusVisible, value and text.

The custom properties live on the box element, so target that element rather than an ancestor:

css
.bb-checkbox .bb-base-checkbox-icon {
    --size: 20px; /* box edge, default 16px */
    --r: 6px; /* corner radius, default 4px */
    --color: #16a34a; /* fill and border when checked */
    --check-color: white; /* the checkmark stroke */
    --ring-color: var(--bb-ring);
}

This is where v2's color prop went, removed with no replacement. Recolor through --color, or through the theme's --bb-primary when the whole product should follow.