Skip to content
RU
Scale
Density
Theme
Accent

Inputs

Checkbox, radio and switch

A checkbox, a radio and a switch. All three are built on a native input: the keyboard, form submission and screen readers come free.

The component
Markup
<label class="inst-checkbox"><input type="checkbox" checked>Cache the result</label>
<label class="inst-radio"><input type="radio" name="mode" checked>In parallel</label>
<label class="inst-radio"><input type="radio" name="mode">Sequentially</label>
<label class="inst-switch"><input type="checkbox" checked>Autostart</label>

Contract

The control lies inside the <label>, so for and id are not needed, and the whole label becomes the tap area.

What Required Why
An <input> inside a <label> yes for and id are not needed, and the tap area becomes the whole label — the one way to meet WCAG 2.5.8 with a glyph of 15px
A shared name on a radio group yes Without it the options are not mutually exclusive, and keyboard navigation by arrows will not work
A <fieldset> with a <legend> around a radio group yes Otherwise a screen reader reads the options but not the question

The input has no class of its own: it is recognised by its type and its parent.

The keyboard is entirely native, and instrument.js intercepts nothing here: Space toggles, in a radio group the arrows traverse the options, and Tab enters and leaves the group.

The border of an unchecked one takes --border-control and holds 3:1: on all three the boundary is the control, and for the same reason a ring is added to the track of a switch. Colour is not the only mark: a checkbox carries a tick, a radio a dot, a switch the position of the thumb.

A switch stays a native checkbox. role="switch" is added if the application wants the state spoken as "on/off" rather than "checked".

Variants

The choice among the three is settled by when the value applies.

Example
Markup
<label class="inst-checkbox"><input type="checkbox" checked>Checkbox</label>
<label class="inst-radio"><input type="radio" name="v" checked>Radio</label>
<label class="inst-switch"><input type="checkbox" checked>Switch</label>
When It applies
Checkbox An independent flag. Any number of them may be checked, zero included On submitting the form
Radio One of several mutually exclusive. Exactly one is always chosen On submitting the form
Switch Switching a mode on and off Immediately, with no "Apply" button

States

Example
Markup
<label class="inst-checkbox"><input type="checkbox">Ordinary</label>
<label class="inst-checkbox"><input type="checkbox" checked>Checked</label>
<label class="inst-checkbox"><input type="checkbox" disabled>Unavailable</label>
<label class="inst-checkbox"><input type="checkbox" checked disabled>Checked and unavailable</label>

The indeterminate state of a checkbox is the indeterminate property; it is set by script alone and has no attribute in the markup:

el.indeterminate = true;   // "some of the nested ones are checked"

API

Classes
NameWhat it does
class
inst-checkboxThe label wrapper of a checkbox
inst-radioThe same for a radio
inst-switchThe same for a switch
Tokens
NameValueWhere from
token
--size-check15pxbase +14: density · scale
--size-switch-w30pxbase +14: density · scale
--size-switch-h18pxbase +14: density · scale
--size-thumb14pxbase +14: density · scale
--border-controllight-dark(oklch(0 0 0 / 0.46), oklch(1 0 0 / 0.38))one across every cell
--accent-marklight-dark(var(--a-4), var(--a-3))one across every cell
--accent-onvar(--n-0)one across every cell
--tracklight-dark(oklch(0 0 0 / 0.10), oklch(1 0 0 / 0.16))one across every cell
--radius-xs4pxbase +4: scale
--radius-full999pxone across every cell
--dur-180msone across every cell

Why the kit works this way — the constitution · Open source under MIT, github.com/keshon/instrument