Checkbox

Native <input type="checkbox"> repainted with appearance: none. The check glyph is an inline-SVG background-image so it tracks currentColor and reads against the primary combination in every theme. Selector excludes .switch-classed inputs so the switch element owns that branch.

Live

Default, checked, disabled

<div style="display: flex; flex-direction: column; gap: 0.5rem;">
<label style="display: inline-flex; align-items: center; gap: 0.5rem;">
  <input type="checkbox" checked /> Subscribe to newsletter
</label>
<label style="display: inline-flex; align-items: center; gap: 0.5rem;">
  <input type="checkbox" /> Receive marketing emails
</label>
<label style="display: inline-flex; align-items: center; gap: 0.5rem;">
  <input type="checkbox" disabled /> Disabled option
</label>
</div>
@use '@adnap/krysalicss' with (
  $feature-list: ('base-reset', 'base-global', 'element-checkbox')
);
Playground

Markup

markup
<label>
  <input type="checkbox" checked> Subscribe to newsletter
</label>
<label>
  <input type="checkbox"> Receive marketing emails
</label>
<label>
  <input type="checkbox" disabled> Disabled option
</label>

When to use

  • Independent on/off choices, including multi-select sets where any subset is valid.
  • For mutually exclusive choices within a group, use Radio.
  • For a single binary toggle that immediately commits a setting, prefer Switch — it's the same native semantics but communicates the on/off-now affordance.

Variables

VariableDefaultNotes
$selector'input[type="checkbox"]:not(.switch)'Excludes switch-classed inputs so the switch plugin owns that branch.
$size1remBox width and height. Square by design.
$border-radius3pxSlightly tighter than $global-border-radius so the box still reads as square at 1rem.
$border-width$global-border-width (1px)Frame stroke.
$focus-outline-width$global-focus-outline-width (2px)Focus-visible ring width.
$focus-outline-style$global-focus-outline-style (solid)Focus ring style.
$focus-outline-offset$global-focus-outline-offset (2px)Gap between border and ring.
$disabled-opacity$global-disabled-opacity (0.6)Routed through controls.disabledControl().
$border-mix$global-border-mix (50%)Border tint vs. currentColor. Clears WCAG 1.4.11 (3:1) on shipped themes.
$check-imageInline SVG check glyph (data URL)Painted via background-image in the :checked rule. Override to swap the glyph; set currentColor stroke to keep theme tracking.

Override example

app.scss
@use '@adnap/krysalicss/element/checkbox' with (
  $size: 1.125rem,
  $border-radius: 4px,
  $border-mix: 60%,
);

Tokens consumed

TokenUsed for
--kc-primary-bg / --kc-primary-fgChecked fill and check-glyph colour.
--kc-focus-ringPainted on :focus-visible. Falls back to --kc-fg then currentColor.
--kc-danger-bgBorder tint when the control matches :user-invalid or [aria-invalid="true"].

Accessibility

  • Anchored on native <input type="checkbox">: keyboard activation (Space), label-click toggling, and assistive-tech semantics ship for free.
  • The :checked state is conveyed by both fill and the check glyph, so the cue isn't colour-only (WCAG 1.4.1).
  • Focus ring uses --kc-focus-ring with a fallback chain into --kc-fg / currentColor, so it stays visible across themes (WCAG 2.4.11).
  • Invalid state routes through :user-invalid + [aria-invalid="true"], which only fires after interaction — untouched required checkboxes do not flag on first paint.
  • The 1rem hit box is below the WCAG 2.5.5 (44 × 44 px) touch-target floor. Wrap the input in a <label> whose hit-area meets the floor so click / tap remains comfortable.
  • Forced-colors mode inherits the browser default for native checkboxes; the framework does not repaint inside (forced-colors: active).

See also

  • Radio — one-of-many choice within a group; pair with checkbox for "select all / select one" choices.
  • Switch — same <input type="checkbox"> markup with the .switch class for binary toggles.
  • Forms overview — full form-control roster.
  • Field wrapper — pairs every form control with label + help text.