Skip to content

HabSwitch ​

A setting that takes effect the moment it is flipped.

Label left, control right — and it takes effect the moment it is flipped
vue
<HabSwitch v-model="autoPublish" label="Auto-publish transcripts" help="Applies immediately." />

If the change needs a Save button, use a HabCheckbox instead. A switch that does nothing until saved is a lie about immediacy, and users act on that lie — they flip it and navigate away.

It reads label-left, control-right, which is the opposite of the other two toggles: a switch is a setting in a list of settings, not an option in a set. Give it a width so the control lands where the reader expects.

Boxed ​

boxed draws the switch as a field: inside HabFieldBox, the box HabInput is drawn in, with the label above and the help below. Use it for a true/false value in a column of fields, where a bare toggle would not line up with the fields around it — and where the value has a kind chosen in a prependAddon segment, which a bare toggle has nothing to put in front of. Outside a form of fields, keep the plain switch: a setting in a list of settings is a row.

on-label and off-label say the state in the box, before the switch, in the product's words. Habitus writes none of its own; without them the box holds the switch alone. density sizes the box as it does a field's, and state="error" marks it as a field's: the error border, the help line in error ink as the message, and aria-invalid on the switch. disabled outranks it.

state has no disabled on a switch, by decision. The fields put availability in state (state="disabled" on HabInput, HabSelect and the pickers); the toggles — HabSwitch, HabCheckbox, HabRadio — have always taken a disabled prop, and a boxed switch keeps it rather than offer two ways to say the same thing. In a column of fields, that is the one prop that reads differently: :disabled="locked" on the switch, :state="locked ? 'disabled' : 'default'" on the fields around it.

density, state, on-label, off-label and prependAddon belong to the box, so they do nothing without boxed — and warn in development, rather than being ignored silently.

Provider
Only Debug Logging is a switch, boxed. The two fields above it are an ordinary HabInput and HabSelect, there to show the box, the label, the kind menu and the value all line up.
vue
<HabSwitch v-model="debug" boxed label="Debug Logging" on-label="true" off-label="false">
	<template #prependAddon>…a HabFieldAddon…</template>
</HabSwitch>
vue
<HabSwitch
	v-model="accepted"
	boxed
	state="error"
	label="Terms"
	help="Accept the terms to continue."
/>

Focused, the box's border turns accent as a field's does, and the track drops its own ring rather than showing two indicators for one focus.

Read-only ​

readonly shows the setting to be read: on or off at full contrast and the label in full ink, but clicking it or pressing Space changes nothing and no update:modelValue is emitted. It stays focusable and is announced as read-only — unlike disabled, which says "not now", is dimmed, and wins when both are set. See HabReadonly for the difference, and for making a whole region read-only at once; :readonly="false" keeps one switch editable inside it.

Read-only: on and off at full contrast, and flipping it changes nothing
vue
<HabSwitch v-model="autoPublish" label="Auto-publish transcripts" readonly />

Accessibility ​

  • A real <input type="checkbox"> carrying role="switch". The native control holds the state; the role only changes how it is announced — as "on"/"off" rather than "checked"/"unchecked".
  • The track is the input's immediately following sibling, which is where base.css draws the focus indicator.
  • The label names the control, the help describes it.
  • The knob's travel is a left transition, which prefers-reduced-motion does not currently suppress — the movement is the affordance rather than decoration, and it is 180ms. Raise it if that proves wrong in use.
  • Give it a label, or an aria-label. Habitus warns when it has neither.

API ​

Props ​

PropTypeDefaultDescription
boxedbooleanfalseDrawn as a field: the switch inside HabFieldBox, the box HabInput is drawn in — the label above it, the help below, state, and the box's prependAddon segment — for a true/false value in a column of fields, where a bare toggle would not line up with them and a kind menu would have nothing to sit in front of. Off by default: a switch in a list of settings is a row, not a field.
density"default" | "compact""default"Box height while boxed, as HabInput's density. Only while boxed; warns otherwise.
disabledbooleanfalse—
helpstringundefinedQuiet second line — what the setting actually does.
labelstringundefinedThe setting's name.
modelValuebooleanfalseOn/off state, for v-model.
offLabelstringundefinedText shown in the box while off. See onLabel.
onLabelstringundefinedText shown in the box while on, before the switch — "true", "Shown". Only while boxed, and warns otherwise; Habitus writes none of its own, so without it the box shows the switch alone.
readonlybooleanundefinedThe setting is shown to be read, not changed: on or off at full contrast, the label in full ink, and clicking or pressing Space changes nothing. Still focusable and announced as read-only — unlike disabled, which says "not now", is dimmed, and wins when both are set. Unset, it follows a surrounding HabReadonly; false keeps this switch editable inside one.
state"default" | "error""default"Validation state while boxed, as a field's: error turns the box's border and the help line to error and sets aria-invalid, so the message in help is announced with it. Availability is disabled, which wins. Only while boxed — a bare switch has no box to mark — and warns otherwise.

Events ​

EventPayloadDescription
update:modelValueon: boolean—

Slots ​

SlotBindingsDescription
prependAddonanyWhile boxed: a control fused to the start of the box — usually a HabFieldAddon as a menu trigger — as on HabInput. Not drawn otherwise, and warns.

Proprietary — internal use only.