Switch
An on/off toggle built on a native checkbox with role="switch".
A switch is a checkbox drawn as a toggle. It is still a native <input type="checkbox">, so form submission, :checked, :disabled and keyboard use (Space) all work. The component adds role="switch", so a screen reader says "on" or "off" instead of "checked".
Use a switch for a setting that takes effect straight away, such as "Notifications". Use a checkbox when the user must submit a form to apply the choice.
Usage
<label class="zui-switch">
<input type="checkbox" role="switch" checked />
Notifications
</label>import { Switch } from '@mrmartineau/zui/react'
<Switch defaultChecked>Notifications</Switch>---
import { Switch } from '@mrmartineau/zui/astro'
---
<Switch checked>Notifications</Switch>import { Switch } from '@mrmartineau/zui/solid'
<Switch checked>Notifications</Switch><script lang="ts">
import { Switch } from '@mrmartineau/zui/svelte'
let enabled = $state(true)
</script>
<Switch bind:checked={enabled}>Notifications</Switch><template>
<Switch v-model="enabled">Notifications</Switch>
</template>
<script setup>
import { ref } from 'vue'
import { Switch } from '@mrmartineau/zui/vue'
const enabled = ref(true)
</script>In a form
Wrap a switch in a Field like any other control. A switch with a description keeps the label short and puts the detail below.
Product news, once a month.
<div class="zui-field">
<label class="zui-switch">
<input type="checkbox" role="switch" name="marketing" aria-describedby="marketing-hint" />
Marketing emails
</label>
<p class="zui-field-description" id="marketing-hint">Product news, once a month.</p>
</div>import { Field, FieldDescription, Switch } from '@mrmartineau/zui/react'
<Field>
<Switch name="marketing" aria-describedby="marketing-hint">
Marketing emails
</Switch>
<FieldDescription id="marketing-hint">Product news, once a month.</FieldDescription>
</Field>---
import { Field, FieldDescription, Switch } from '@mrmartineau/zui/astro'
---
<Field>
<Switch name="marketing" aria-describedby="marketing-hint">
Marketing emails
</Switch>
<FieldDescription id="marketing-hint">Product news, once a month.</FieldDescription>
</Field>import { Field, FieldDescription, Switch } from '@mrmartineau/zui/solid'
<Field>
<Switch name="marketing" aria-describedby="marketing-hint">
Marketing emails
</Switch>
<FieldDescription id="marketing-hint">Product news, once a month.</FieldDescription>
</Field><script lang="ts">
import { Field, FieldDescription, Switch } from '@mrmartineau/zui/svelte'
</script>
<Field>
<Switch name="marketing" aria-describedby="marketing-hint">
Marketing emails
</Switch>
<FieldDescription id="marketing-hint">Product news, once a month.</FieldDescription>
</Field><template>
<Field>
<Switch name="marketing" aria-describedby="marketing-hint">
Marketing emails
</Switch>
<FieldDescription id="marketing-hint">Product news, once a month.</FieldDescription>
</Field>
</template>
<script setup>
import { Field, FieldDescription, Switch } from '@mrmartineau/zui/vue'
</script>Disabled
<label class="zui-switch">
<input type="checkbox" role="switch" disabled checked />
Disabled on
</label>
<label class="zui-switch">
<input type="checkbox" role="switch" disabled />
Disabled off
</label>import { Switch } from '@mrmartineau/zui/react'
<Switch disabled defaultChecked>Disabled on</Switch>
<Switch disabled>Disabled off</Switch>---
import { Switch } from '@mrmartineau/zui/astro'
---
<Switch disabled checked>Disabled on</Switch>
<Switch disabled>Disabled off</Switch>import { Switch } from '@mrmartineau/zui/solid'
<Switch disabled checked>Disabled on</Switch>
<Switch disabled>Disabled off</Switch><script lang="ts">
import { Switch } from '@mrmartineau/zui/svelte'
</script>
<Switch disabled checked>Disabled on</Switch>
<Switch disabled>Disabled off</Switch><template>
<Switch disabled checked>Disabled on</Switch>
<Switch disabled>Disabled off</Switch>
</template>
<script setup>
import { Switch } from '@mrmartineau/zui/vue'
</script>Size and colour
Size scales with --zui-switch-size. Set it on the element or a wrapper.
<label class="zui-switch" style="--zui-switch-size: 1.1em">
<input type="checkbox" role="switch" checked />
Small
</label>
<label class="zui-switch" style="--zui-switch-size: 2em; --zui-switch-accent: var(--color-success)">
<input type="checkbox" role="switch" checked />
Large, success colour
</label>import { Switch } from '@mrmartineau/zui/react'
<Switch defaultChecked style={{ '--zui-switch-size': '1.1em' }}>Small</Switch>
<Switch defaultChecked style={{ '--zui-switch-size': '2em', '--zui-switch-accent': 'var(--color-success)' }}>
Large, success colour
</Switch>---
import { Switch } from '@mrmartineau/zui/astro'
---
<Switch checked style="--zui-switch-size: 1.1em">Small</Switch>
<Switch checked style="--zui-switch-size: 2em; --zui-switch-accent: var(--color-success)">
Large, success colour
</Switch>import { Switch } from '@mrmartineau/zui/solid'
<Switch checked style={{ '--zui-switch-size': '1.1em' }}>Small</Switch>
<Switch checked style={{ '--zui-switch-size': '2em', '--zui-switch-accent': 'var(--color-success)' }}>
Large, success colour
</Switch><script lang="ts">
import { Switch } from '@mrmartineau/zui/svelte'
</script>
<Switch checked style="--zui-switch-size: 1.1em">Small</Switch>
<Switch checked style="--zui-switch-size: 2em; --zui-switch-accent: var(--color-success)">
Large, success colour
</Switch><template>
<Switch checked style="--zui-switch-size: 1.1em">Small</Switch>
<Switch checked style="--zui-switch-size: 2em; --zui-switch-accent: var(--color-success)">
Large, success colour
</Switch>
</template>
<script setup>
import { Switch } from '@mrmartineau/zui/vue'
</script>Accessibility
- It is a native checkbox, so Space toggles it and it joins form submission.
role="switch"makes screen readers announce on/off. The wrappers add it for you. In plain HTML, add it yourself.- Write the label as the setting, not the action: "Notifications", not "Turn on notifications". The state is announced separately.
- In forced-colours mode (Windows High Contrast) the track and thumb switch to system colours.
CSS custom properties
Set these on :root, on a wrapper, or on the element. See Component custom properties.
| Property | Default | Description |
|---|---|---|
--zui-switch-size |
1.5em |
Track height. Track width is 1.8 times this |
--zui-switch-radius |
var(--radius-full) |
Border radius of the track and thumb |
--zui-switch-accent |
var(--color-accent) |
Track colour when on |
--zui-switch-track |
oklch(from var(--color-text) l c h / 25%) |
Track colour when off |
--zui-switch-thumb |
var(--color-background) |
Thumb colour |