Checkbox

Free

Accessible checkboxes with 5 variants, 3 sizes, indeterminate state and ControlValueAccessor support.

</>sv-checkbox checkboxformselection
import { SvCheckboxComponent } from '@styloviz/forms';

Demo & usage

Basic usage

Usage example — open the gallery for the full interactive demo.

An interactive demo for this component lives in the gallery.

Open live demo

More examples

Indeterminate "select all"

Use indeterminate for a parent that controls a group.

example.htmlAngular
<sv-checkbox label="Select all" [indeterminate]="someSelected" [(ngModel)]="allSelected" />

With description

example.htmlAngular
<sv-checkbox label="Email me" description="Weekly product digest." [(checked)]="subscribe" />

Required + reactive forms

Works with formControlName via ControlValueAccessor; required sets native required + aria-required, and name/value enable plain form submission.

example.htmlAngular
<sv-checkbox
  formControlName="acceptTerms"
  label="I accept the terms"
  description="You must accept to continue."
  [required]="true"
  name="acceptTerms"
  value="yes" />

When to use

  • Selecting any number of independent options, or a single opt-in (terms, consent).
  • You need an indeterminate parent for "select all / partial" patterns.
  • For mutually exclusive options use a Radio Group; for instant settings use a Toggle.

Async states

  • error

Server writes

Writes
  • changeRequest

Each payload carries a requestKey — send it with the request so a retry cannot apply the same change twice.

Rollback
Call event.rollback() when the save fails — the change is already on screen, and this is what puts it back.
While saving
Bind pending — with two writes outstanding there is no single state to roll back to, so the action stays locked until the first one lands.
On failure
Pass the failure to error to show it in place — without taking away what the user was working on.

Inputs

NameTypeDefaultDescription
checkedboolean (two-way)falseCurrent checked state. Use `[(checked)]` for two-way binding.
labelstring''Visible label text.
descriptionstring''Smaller descriptor line beneath the label.
inputIdstring''Explicit id forwarded to the native input. Auto-generated when omitted.
ariaLabelstring''`aria-label` override — required when there is no visible `label` so screen readers can still announce the control's purpose.
sizeCheckboxSize'md'Size of the checkbox.
variantCheckboxVariant'primary'Active color variant.
indeterminatebooleanfalseShow an indeterminate (dash) state.
disabledbooleanfalseDisable interaction.
requiredbooleanfalseMark the control as required (sets native `required` + `aria-required`).
namestring''`name` for native form submission.
valuestring''`value` submitted with `name` when checked (grouped checkboxes).
autofocusbooleanfalseAutofocus the control on render.
customClassstring''Additional CSS classes on the root wrapper.
errorMessagestring''Client-side validation message. "You must accept the terms" had nowhere to go before this: the control rendered a box and a label and nothing else, so a form could reject it with no way to say why.
serverErrorSvServerErrornullA rejection that came back from the server about THIS value. Self-expires as soon as the value changes — see `createFieldError`.
pendingbooleanfalseThe user's change is applied on screen and its write is in flight. Not `disabled`: the tick the user just placed stays at full strength — fading a value the user chose reads as "this did not take" — and the box stays focusable. It refuses further changes while true, which is what makes `rollback()` unambiguous. Bind it for the whole round-trip.
errorSvDataError | string | nullnullThe write failed. Accepts an `SvDataError` or a plain string. Not `serverError`. That is the field-validation contract and it expires on the next value change — and a rollback IS a value change, so a failure routed through it would vanish in the same tick the box un-ticked. The user would watch it revert and be told nothing. Call `rollback()` as well: a message beside a box still showing the state that failed is worse than either alone.

Outputs

EventPayload typeDescription
changedbooleanEmits the new boolean state after every change.
changeRequestSvCheckboxChangeThe same change, carrying what a server write needs: an idempotency key and a guarded `rollback()`. Additive — `changed` still fires first, and a checkbox that only writes to local state has no use for this.
checkedChangebooleanEmits the new `checked` whenever it changes — the output half of `[(checked)]`.

Customization & theming

  • Choose a `variant` (primary, success, warning, danger, neutral) and `size` (sm, md, lg) to match your form density.
  • Supports an `indeterminate` state for "select all" patterns and a `description` sub-label.
  • Add `required`, `name` / `value`, `autofocus`; two-way bind with `[(checked)]` or a reactive control and listen to `(changed)`.

Best practices

  • Do: Keep labels positive and parallel so the checked meaning is obvious.
  • Do: Drive "select all" with the `indeterminate` state when only some children are checked — and CLEAR it in your `(changed)` handler. It is an input this component cannot clear, so a mixed checkbox that is clicked stays mixed: the box fills in, and a screen reader still announces `aria-checked="mixed"`. A dev-only warning fires the first time this happens.
  • Don't: Don't treat `indeterminate` as a third VALUE. It is a visual state. `checked` is the value — a boolean, the only thing ControlValueAccessor ever writes or reads — so a parent that stores `null` for "some children selected" gets `false` back, silently, because `writeValue(null)` is `!!null`. There is no third state to store it in.
  • Don't: Don't use a checkbox for an action that takes effect immediately — use a Toggle.
  • Tip: Set `ariaLabel` when there is no visible `label` (e.g. a table row selector).

Accessibility

  • A real, focusable native checkbox (visually hidden) owns keyboard activation (Space) and announces role/state — including `aria-checked="mixed"` for indeterminate.
  • A unique id is generated automatically so the `<label for>` association works without `inputId`; clicking the label toggles the box and the focus ring is clearly visible.
  • The `description` is linked via `aria-describedby`; `required` sets `aria-required`; use `ariaLabel` when there is no visible label.
  • Implements ControlValueAccessor — works with template & reactive forms; programmatic `formControl.disable()` disables the native input.
  • The value and the appearance are allowed to disagree, and only one of them is ever submitted: `aria-checked="mixed"` and the dash render the `indeterminate` input, while the form receives the boolean `checked`.

Feedback on Checkbox

Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.

Loads giscus.app and GitHub — nothing is fetched until you click.