Fylgja Input Switch

A switch turns a single setting on or off, and applies immediately rather than on submit. It is a native <input type="checkbox"> with role="switch", which Fylgja styles into a track and thumb. Because it is still a checkbox, it takes part in forms, restores on reload, and keeps its keyboard behavior for free.

Preview

<label class="flex gap-sm">
	<input type="checkbox" role="switch" checked />
	Email notifications
</label>

Usage

  • Write a plain <input type="checkbox" role="switch"> and give it a label. No classes are needed; Fylgja styles the control from the attribute.
  • Wrapping the control in its <label> (as above) labels it without an id. Use a separate <label for> when the label and the control sit apart in the layout.
  • checked sets the initial state, disabled greys the control out.
  • The track and thumb follow the same variables as a checkbox, so --control-size sizes the height and thumb, and --switch-size sets the width.

Anatomy

Part Responsibility
<input type=checkbox> The control, its state, and the submitted value.
role="switch" Announces the state as on/off and triggers Fylgja’s style.
<label> Names the setting the switch controls.

Accessibility

  • role="switch" changes how the state is announced, from “checked” to “on” or “off”. Everything else, the tab stop, the Space toggle, and the form value, stays native.
  • Label the switch by what it turns on, not by its current state. “Email notifications” works, “Notifications are on” does not.
  • Do not rely on color alone. The thumb also moves, so the state stays readable without color.
  • If flipping the switch triggers a slow save, tell the reader the result in a live region such as a Toast, not with a hidden state change.

Variants

Larger switch

Both size variables cascade, so setting them on a wrapper sizes the control inside it.

<label class="flex gap-sm" style="--control-size: 1.75em; --switch-size: 3em">
	<input type="checkbox" role="switch" checked />
	Larger switch
</label>

Examples

Settings list

A run of switches in a list, each label pushed against its control.

<ul role="list" class="flex-col gap-sm">
	<li class="flex gap align">
		<label for="switch-replies">Comment replies</label>
		<input type="checkbox" role="switch" id="switch-replies" checked />
	</li>
	<li class="flex gap align">
		<label for="switch-digest">Weekly digest</label>
		<input type="checkbox" role="switch" id="switch-digest" />
	</li>
	<li class="flex gap align">
		<label for="switch-sms">Text messages</label>
		<input type="checkbox" role="switch" id="switch-sms" disabled />
	</li>
</ul>

FAQ

Why a checkbox instead of a button?

A switch is a two-state control that belongs to a form, so it should submit a value and restore with the form. A checkbox does that natively. The role=switch attribute only changes how it is announced, from "checked" to "on/off".

Does the switch need a submit button?

That depends on your form. A switch inside a settings form that saves on change needs script to send the value. A switch inside a normal form submits like any other checkbox, so nothing extra is required.

How do I make the switch bigger?

Set --control-size for the height of the track and thumb, and --switch-size for the width. Both take any length, so a value such as 1.75em and 3em gives a larger control that still scales with the font size.

Can I put a label on both sides, like Off and On?

Avoid it. Two labels make it ambiguous which one names the control and which one describes the state. Use one label for what the setting does and let the switch itself communicate on or off.