Checkbox
A box for a single on/off choice, with an indeterminate state for partial selections.
Playground
<Checkbox
error=""
helper="Required for signup"
label="Accept terms"
/>01 Examples
Task list
bind:checked writes every click straight back into your data, and onCheckedChange runs alongside it with the new boolean. Both hear the user only, so assigning checked in your own code stays silent.2 of 4 done
<div
class="border-border-subtle bg-surface-elevated flex w-full max-w-sm flex-col gap-1 rounded-2xl border p-4"
>
{#each tasks as task (task.id)}
<Checkbox bind:checked={task.done} label={task.label} />
{/each}
<p class="text-text-tertiary mt-2 text-xs">{doneCount} of {tasks.length} done</p>
</div>Select all
indeterminate on its own, so keep deriving that flag from your data rather than toggling it.{@const allGranted = granted.length === scopes.length}
{@const someGranted = granted.length > 0 && !allGranted}
<div
class="border-border-subtle bg-surface-elevated flex w-full max-w-sm flex-col gap-2 rounded-2xl border p-4"
>
<Checkbox
label="All scopes"
checked={allGranted}
indeterminate={someGranted}
onCheckedChange={(on) => (granted = on ? [...scopes] : [])}
/>
<div class="border-border-subtle ml-6 flex flex-col gap-2 border-l pl-4">
{#each scopes as scope (scope)}
<Checkbox
label={scope}
checked={granted.includes(scope)}
onCheckedChange={(on) =>
(granted = on ? [...granted, scope] : granted.filter((s) => s !== scope))}
/>
{/each}
</div>
</div>Consent in a form
name submits the box as value (on unless you set your own), and only while it is checked. An unchecked box is absent from the FormData altogether, so data.get('terms') comes back null rather than off. error takes the helper text's place, reddens the message and sets aria-invalid.<form class="flex w-full max-w-sm flex-col gap-3" onsubmit={handleConsent}>
<Checkbox
name="terms"
label="I accept the terms of service"
helper="You can withdraw your consent at any time."
error={consentError}
/>
<Button type="submit" size="sm" class="self-start">Continue</Button>
{#if consented}
<p class="text-success text-xs">Consent recorded.</p>
{/if}
</form>02 Micro-Interactions (Mint)
Two presets at once
bounce also fires when the click lands on the label text. A single preset is a string, an array runs them together.<Checkbox mint="glow" label="Glow on hover" checked intent="success" />
<Checkbox mint={['scale', 'glow']} label="Scale and glow together" checked intent="danger" />03 Customization
Neon gradient
data-state of unchecked, checked or indeterminate, and that is what this gradient keys off. Radius tier, focus ring and check-draw animation keep coming from the defaults underneath.{@const neon = {
box: 'border-white/25 bg-transparent text-white group-hover:border-white/40 data-[state=checked]:border-transparent data-[state=checked]:bg-linear-to-br data-[state=checked]:from-violet-500 data-[state=checked]:to-fuchsia-500 data-[state=checked]:shadow-[0_0_14px_rgba(217,70,239,0.65)]',
label: 'text-white/90'
}}
<Checkbox checked label="Notify me about new releases" slotClasses={neon} />
<Checkbox label="Join the beta program" slotClasses={neon} />This is one of five ways to restyle a block. See Customization for class, slotClasses, unstyled, preset and provider-level overrides.
04 Accessibility
Native semantics
The control is a real <input type="checkbox">,
so it brings the form and assistive-technology behaviour with it: name and value reach FormData while the box is checked, and required is the native constraint, browser message
included. indeterminate additionally sets aria-checked="mixed".
Labels and descriptions
The whole row is a <label>, so a click on the
text toggles the box. The label prop fills that text,
and where a design leaves it out, an aria-label passed to the component reaches the input and names it. Helper or error text links to the input
through aria-describedby: an error takes the helper's place, sets aria-invalid and is the one announced through role="alert".
Keyboard
Tab to focus, Space to toggle. The focus ring shows for keyboard users only.
05 API Reference
22 propsProp | Type | Default | Description | |
|---|---|---|---|---|
checked | boolean | — | Current checked state. Supports two-way binding via bind:checked. | |
class | string | — | Extra classes merged onto the wrapper element. | |
disabled | boolean | — | Prevent interaction and dim the control. | |
error | string | — | Error message that replaces helper, styles the message red, and sets aria-invalid on the input. | |
helper | string | — | Hint text shown below the control. Hidden when error is set. | |
id | string | — | Explicit id to link <label> and <input>. Auto-generated if omitted. | |
indeterminate | boolean | — | Visual-only third state showing a dash icon. Resets to unchecked on next
user toggle. Does not affect the submitted form value. Supports
bind:indeterminate. | |
intent variant | dangerneutralprimarysecondary +2 more | primary | Controls the color theme and semantic meaning of the Checkbox. Affects the overall appearance and user perception. Available options: danger, neutral, primary, and 3 more. | |
label | string | — | Text label displayed to the right of the checkbox box. | |
mint | MintProp | 'none' | Micro-interaction preset applied to the box (the visible control, not
the sr-only input). Click-triggered engine mints listen on the
surrounding label; ripple ignites only on the box itself. Only
applies while not disabled. | |
name | string | — | The name attribute of the underlying <input>. Used for form submission. | |
onCheckedChange | (checked: boolean) => void | — | Fired after the checked state changes. Receives the new checked value. | |
preset | string | — | Apply a named preset registered via <BlocksProvider presets={{ Checkbox: {...} }}>.
Prefer this over class overrides when the requested look falls outside the
semantic intent palette — presets keep hover/active/dark-mode logic coherent
and make the custom look reusable across the project. | |
required | boolean | — | Mark the native input as required for form validation. Also draws the
family's required marker beside the label text (never beside the box); it
needs a label to sit after, and slotClasses.requiredMark: 'hidden'
takes it away. | |
size variant | lgmdsmxs | md | Controls the dimensions, padding, and text size of the Checkbox. Affects the component's physical footprint. Available options: lg, md, sm, xs. | |
slotClasses | Partial<Record<CheckboxSlots, string>> | — | Per-slot class overrides merged with (or replacing, when unstyled) the
default styles. Slots: wrapper (root — what class also targets) | control |
box | icon | label | requiredMark | message. | |
tier | InteractiveTier | — | Semantic radius tier. Default modify — checkbox is an input-tap
surface. Inherited from TierContext when omitted; falls back to
modify outside of any tier-aware container. | |
unstyled | boolean | — | Strip all default variant classes. Use with slotClasses for a fully custom look. The box exposes data-state for conditional styling. | |
value | string | — | The value submitted when checked. Defaults to 'on'. | |
variant variant | filledghostoutlined | outlined | Controls the visual style and presentation of the Checkbox. Determines the component's visual treatment. Available options: filled, ghost, outlined. | |
...CheckboxVariants variant | VariantProps | — | Styling variants from CheckboxVariants | |
...HTMLInputAttributes inherited | HTMLAttributes | — | HTML attributes (excluding: 'type' | 'size' | 'checked' | 'class' | 'children') |
06 Types
Local type definitions used by this component.
Name | Kind | Category | Used by | Description | |
|---|---|---|---|---|---|
CheckboxProps | interface | props | 0 | — | |
CheckboxVariants | type | variant | 0 | — | |
CheckboxSlots | type | variant | 0 | Slot names derived from the tv() config above — single source of truth for slotClasses. | |
MintProp | type | helper | 1 | — | |
InteractiveTier | type | helper | 1 | Semantic radius tier for interactive surfaces (3-tier system).
- commit → r-human (CTA, identity, status declarations)
- modify → r-interactive (fields, navigation, secondary actions)
Container components (Card, Alert, Toolbar surface, …) live in a third
tier contain (r-structure) which is **not** part of this propagation
context — those surfaces are always r-structure by design and have no
tier-flip use case. | |
MintName | type | helper | 0 | A mint name: a built-in (autocompleted), 'none' to disable, or any
consumer-registered name. (string & {}) keeps the registry open — a
custom name still type-checks, it just isn't suggested. A typo therefore
also still compiles (it resolves like an unregistered custom name and
warns at runtime); the union buys completion and docs, not validation. | |
MintConfig | interface | helper | 0 | — | |
BuiltinMintName | type | helper | 0 | Built-in mint names as a literal union, so the mint prop autocompletes
across every component — the single list the hand-curated playground knobs
and docs used to drift away from. |
07 Installation
Import
import { Checkbox } from '@urbicon-ui/blocks';