Skip to content
View as Markdown

Inputs

Checkbox

A native checkbox with an adjacent label and description.

Import

TSX
import { Checkbox, Field } from "@loamui/core";

Usage

Every example has a CSS tab. That’s the real, complete stylesheet for the component: plain, static CSS, with nothing running in the browser.

Basic usage

One self-contained opt-in. The label is a complete statement of what ticking the box does, and the box starts unticked so every tick is a deliberate act.

Checked

defaultChecked starts the box ticked for a form the browser owns; checked with onChange holds it yourself. A ticked start is for a setting that is already on, never for consent.

With description

Field.Description is helper text under the label, joined to the box through aria-describedby, so the consequence of ticking is read with the choice.

Helps us improve the product. You can opt out anytime.

Disabled

disabled reaches the native input: the row is dimmed and skipped by Tab. A ticked, disabled box shows a setting that is on and not the user's to change here. Disabled is detected on the input (:has(input:disabled)), never declared on the row.

Error state

Set invalid on Field.Root and compose Field.Error before the checkbox for its announced message.

Composed inside a Field

Without label or description props, Checkbox renders the bare input: it reads its id, aria-describedby and aria-invalid from the surrounding Field, so the label lives on Field.Label and nothing wires them by hand.

A short summary, once a week.

Guidance

When to use it

  • For a single on/off choice (accept terms, stay signed in).
  • For selecting any number of options from a list: group related checkboxes in a Fieldset.
  • Inside a Field for full control, compose the bare box so labels never nest: <Field.Label><Checkbox /> …</Field.Label>.

When not to

  • For one choice among several mutually exclusive options: use Radio.
  • For an instant on/off toggle that takes effect immediately: use Switch.

Accessibility

  • Renders a real <input type="checkbox"> wrapped by its label, so clicking the text toggles it and the state is announced natively.
  • Supports an indeterminate (mixed) visual for a 'select all' parent, set on the DOM node. It describes the display while the submitted value remains checked or unchecked.
  • The checkbox reads label, description and validation wiring from Field.Root or Field.Item. Use Field.Item for each option in a group so its label and description remain independent.
  • Disabled is detected on the native input (:has(input:disabled) on the row), never declared on a wrapper.
  • Errors come from Field composition: wrap the checkbox in a Field.Root and add a Field.Error before the control, and set invalid on Root for validation. The message is announced.
  • Group multiple checkboxes under a Fieldset so the legend names the set in the accessibility tree.

How it works

A native checkbox, styled by accent-color

This is a plain <input type="checkbox">. No custom SVG box. The elements layer paints it with the platform's own accent-color (the neutral primary), so the checked and indeterminate marks, keyboard behaviour and forced-colours support all come from the browser. The component adds Field wiring and the invalid affordance. Field.Label provides a comfortable click target. Clicking that label toggles the native control. A context region recolours it because accent-color follows the primary token.

One box or a group

A single checkbox is for one self-contained agreement or opt-in whose label is a complete statement (“Agree to the terms of service”). Several related options belong in a Fieldset whose legend asks the question. Because checkboxes and radios look alike, say in the legend or description that users can select all that apply.

Write the label positively

The label states what happens when the box is ticked, in positive, unambiguous words: “Send me email updates”, never “Don't send me emails”. A negated label makes ticking mean refusing and unticking a double negative, and users acting quickly resolve it wrong.

Leave boxes unticked

A pre-ticked box gets submitted by everyone who never read it, so the data records a choice nobody made, and for consent it records nothing at all. Start unticked, so every tick is a deliberate act.

Error messages

Say what happened and how to fix it, in the words of the question itself. See the writing guidance on the Field page.

SituationMessage
A required agreement is untickedSelect [whatever the checkbox label states] to continue
Nothing in a required group is selectedSelect [whatever the legend asks for]
Too many options are selectedSelect no more than [N] [things]

Props

PropTypeDefaultDescription
label / descriptionReactNode—Optional content for a complete labelled row. Omit both when composing the control inside Field.Label.
wrapperPropsOmit<PartProps<"div">, "children">—Props for the row rendered with label or description. className, style and ref on the component target its native input.
indeterminatebooleanfalseSets the native partially checked state.
...othersInputHTMLAttributes—Native input props except type and size, including ref. Compose labels and descriptions through Field.