Switch
hc-switch is applied to a standard
<input type="checkbox" role="switch">. The underlying input keeps
every native behaviour — Space toggles, the value participates in
form submission, screen readers announce it as a switch via the
role override — and only the visual chrome is replaced via
appearance: none.
Also known as: toggle, toggle switch.
Basic HTML
Section titled “Basic HTML”<label class="hc-switch-label"> <input class="hc-switch" type="checkbox" role="switch" checked> Email notifications</label>
<label class="hc-switch-label"> <input class="hc-switch" type="checkbox" role="switch"> SMS notifications</label>.hc-switch-label is an inline-flex helper that clusters the
switch and its text so a click on the label toggles the input.
Variants
Section titled “Variants”data-variant accepts success, warning, and error for
non-default checked-state tints — the same trio as
checkbox and
radio. The default (omitted)
variant uses the active color theme’s primary fill.
<label class="hc-switch-label"> <input class="hc-switch" type="checkbox" role="switch" checked> Default (themed)</label><label class="hc-switch-label"> <input class="hc-switch" type="checkbox" role="switch" data-variant="success" checked> Success</label><label class="hc-switch-label"> <input class="hc-switch" type="checkbox" role="switch" data-variant="warning" checked> Warning</label><label class="hc-switch-label"> <input class="hc-switch" type="checkbox" role="switch" data-variant="error" checked> Error</label>data-size accepts sm, md (default), and lg.
<label class="hc-switch-label"> <input class="hc-switch" type="checkbox" role="switch" data-size="sm" checked> Small</label><label class="hc-switch-label"> <input class="hc-switch" type="checkbox" role="switch" checked> Default</label><label class="hc-switch-label"> <input class="hc-switch" type="checkbox" role="switch" data-size="lg" checked> Large</label>States
Section titled “States”<label class="hc-switch-label"> <input class="hc-switch" type="checkbox" role="switch" disabled> Disabled (off)</label><label class="hc-switch-label"> <input class="hc-switch" type="checkbox" role="switch" disabled checked> Disabled (on)</label>htmx usage
Section titled “htmx usage”The switch participates in form submission like any native
checkbox. For instant toggles, listen to change:
<label class="hc-switch-label"> <input class="hc-switch" type="checkbox" role="switch" name="notifications" data-hx-post="/preferences/notifications" data-hx-trigger="change"> Email notifications</label>When the switch is part of a plain form post and the server binds a
declared boolean, remember that an unchecked switch submits nothing —
use the blessed boolean-field pattern
(hidden false + switch true under one name); it applies to
hc-switch verbatim.
The switch is the action
Section titled “The switch is the action”A feature flag, a per-user preference: the switch is the save, there is
no Save button. Rather than giving each switch its own request, keep the
form as the unit — it is what the
mutating-form recipe
already owns (target, swap, 422 re-render) — and let the switch post it
the moment it changes with data-hc-submit-on-change:
<form method="post" action="/flags" data-hx-post="/flags" data-hx-target="closest .hc-card" data-hx-swap="outerHTML" data-hx-sync="this:replace"> <label class="hc-switch-label"> <input type="hidden" name="beta" value="false"> <input class="hc-switch" type="checkbox" role="switch" name="beta" value="true" data-hc-submit-on-change> Beta features </label> <noscript><button class="hc-button" type="submit">Save</button></noscript></form>On change the behavior calls form.requestSubmit(): constraint
validation runs, the form’s submit event fires, and htmx takes the
request from there — so hx-sync, the
dirty guard, the
CSRF header and
data-hc-close-dialog-on-success all apply, exactly as if a Save button
had been pressed. The attribute may also sit on a <fieldset>: any
descendant’s change bubbles to it, so a settings group submits as one
form.
Two things the behavior decides for you, once: a control that carries
its own htmx verb (the data-hx-post shape above) is left alone, and
so is a form whose hx-trigger already includes change — either way
htmx is driving, and a second submit would double-fire. Without
JavaScript the switch simply does not auto-submit; the <noscript> Save
button keeps the no-JS path honest.
Accessibility
Section titled “Accessibility”- Always pair the input with a label. Either wrap both in
<label class="hc-switch-label">or use a separate<label for>associated by id. role="switch"is the semantic role for a binary on/off toggle — prefer it over a plain checkbox so screen readers announce “switch, on/off” rather than “checkbox, checked/unchecked”. The underlying element stays an<input type="checkbox">for form serialisation; the ARIA role override only changes how it’s announced.- Don’t remove the focus outline. The component replaces the
default outline with a visible box-shadow ring driven by
--hc-color-focus-ring. - The thumb-slide transition respects
prefers-reduced-motion: reduce— motion is suppressed when the user has opted out.
Theming tokens
Section titled “Theming tokens”Component tokens (in component.tokens.json):
| Token path | Purpose |
|---|---|
switch.width / height |
Track dimensions. |
switch.thumb-size |
Diameter of the moving thumb. |
switch.padding |
Gap between thumb and track edge. |
switch.bg / thumb-bg |
Off-state colors. |
switch.checked-bg / checked-border |
Default-variant on-state. |
switch.success-checked-bg / warning-checked-bg / error-checked-bg |
Variant on-state. |
switch.disabled-bg |
Background when disabled. |
switch.label-gap |
Gap between switch and its text. |
switch.sm.* / lg.* |
Dedicated sm / lg overrides. |
CSS variables
Section titled “CSS variables”Show the generated CSS variables
--hc-switch-width | -height | -thumb-size | -padding--hc-switch-border-width | -border--hc-switch-bg | -thumb-bg--hc-switch-checked-bg | -checked-border--hc-switch-success-checked-bg | -warning-checked-bg | -error-checked-bg--hc-switch-disabled-bg | -label-gap--hc-switch-sm-width | -sm-height | -sm-thumb-size--hc-switch-lg-width | -lg-height | -lg-thumb-size--hc-color-focus-ring (inherited from data-color)