Checkbox
FreeAccessible checkboxes with 5 variants, 3 sizes, indeterminate state and ControlValueAccessor support.
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 demoMore examples
Indeterminate "select all"
Use indeterminate for a parent that controls a group.
<sv-checkbox label="Select all" [indeterminate]="someSelected" [(ngModel)]="allSelected" />
With description
<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.
<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
errorto show it in place — without taking away what the user was working on.
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| checked | boolean (two-way) | false | Current checked state. Use `[(checked)]` for two-way binding. |
| label | string | '' | Visible label text. |
| description | string | '' | Smaller descriptor line beneath the label. |
| inputId | string | '' | Explicit id forwarded to the native input. Auto-generated when omitted. |
| ariaLabel | string | '' | `aria-label` override — required when there is no visible `label` so screen readers can still announce the control's purpose. |
| size | CheckboxSize | 'md' | Size of the checkbox. |
| variant | CheckboxVariant | 'primary' | Active color variant. |
| indeterminate | boolean | false | Show an indeterminate (dash) state. |
| disabled | boolean | false | Disable interaction. |
| required | boolean | false | Mark the control as required (sets native `required` + `aria-required`). |
| name | string | '' | `name` for native form submission. |
| value | string | '' | `value` submitted with `name` when checked (grouped checkboxes). |
| autofocus | boolean | false | Autofocus the control on render. |
| customClass | string | '' | Additional CSS classes on the root wrapper. |
| errorMessage | string | '' | 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. |
| serverError | SvServerError | null | A rejection that came back from the server about THIS value. Self-expires as soon as the value changes — see `createFieldError`. |
| pending | boolean | false | The 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. |
| error | SvDataError | string | null | null | The 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
| Event | Payload type | Description |
|---|---|---|
| changed | boolean | Emits the new boolean state after every change. |
| changeRequest | SvCheckboxChange | The 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. |
| checkedChange | boolean | Emits 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.