Switch
Binary control primitive that reflects switch semantics for checked state.
Default
With checked omitted, Switch starts off and enabled.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-switch/ui-switch.html"
>
<ui-switch>Frodo is available</ui-switch><script setup>
import { UiSwitch } from "@threadlabs/looma";
</script>
<template>
<UiSwitch>Frodo is available</UiSwitch>
</template>import { UiSwitch } from "@threadlabs/looma";
export default function Example() {
return <UiSwitch>Frodo is available</UiSwitch>;
}/** @jsxImportSource preact */
import { UiSwitch } from "@threadlabs/looma";
export default function Example() {
return <UiSwitch>Frodo is available</UiSwitch>;
}<script>
import { UiSwitch } from "@threadlabs/looma";
</script>
<UiSwitch>Frodo is available</UiSwitch>import { Component } from "@angular/core";
import { UiSwitch } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiSwitch],
template: `
<label
ui-switch
>Frodo is available</label>
`,
})
export default class Example {}checked and disabled
Default-checked supplies initial on state while disabled prevents interaction.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-cluster/ui-cluster.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-switch/ui-switch.html"
>
<ui-cluster
gap="l"
>
<ui-switch
checked
>Checked</ui-switch>
<ui-switch
disabled
>Disabled</ui-switch>
</ui-cluster><script setup>
import { UiCluster, UiSwitch } from "@threadlabs/looma";
</script>
<template>
<UiCluster
gap="l"
>
<UiSwitch
:checked="true"
>Checked</UiSwitch>
<UiSwitch
:disabled="true"
>Disabled</UiSwitch>
</UiCluster>
</template>import { UiCluster, UiSwitch } from "@threadlabs/looma";
export default function Example() {
return <UiCluster
gap="l"
>
<UiSwitch
checked
>Checked</UiSwitch>
<UiSwitch
disabled
>Disabled</UiSwitch>
</UiCluster>;
}/** @jsxImportSource preact */
import { UiCluster, UiSwitch } from "@threadlabs/looma";
export default function Example() {
return <UiCluster
gap="l"
>
<UiSwitch
checked
>Checked</UiSwitch>
<UiSwitch
disabled
>Disabled</UiSwitch>
</UiCluster>;
}<script>
import { UiCluster, UiSwitch } from "@threadlabs/looma";
</script>
<UiCluster
gap="l"
>
<UiSwitch
checked
>Checked</UiSwitch>
<UiSwitch
disabled
>Disabled</UiSwitch>
</UiCluster>import { Component } from "@angular/core";
import { UiCluster, UiSwitch } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiCluster, UiSwitch],
template: `
<div
ui-cluster
[gap]="'l'"
>
<label
ui-switch
[checked]="true"
>Checked</label>
<label
ui-switch
[disabled]="true"
>Disabled</label>
</div>
`,
})
export default class Example {}name, value, and required
Name and value are what the form submits when the switch is on (value defaults to "on"), and a switch that is off sends nothing; required blocks submission until it is switched on.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-switch/ui-switch.html"
>
<form>
<ui-switch
name="alerts"
value="enabled"
required
>Email alerts</ui-switch>
</form><script setup>
import { UiSwitch } from "@threadlabs/looma";
</script>
<template>
<form>
<UiSwitch
name="alerts"
value="enabled"
:required="true"
>Email alerts</UiSwitch>
</form>
</template>import { UiSwitch } from "@threadlabs/looma";
export default function Example() {
return <form>
<UiSwitch
name="alerts"
value="enabled"
required
>Email alerts</UiSwitch>
</form>;
}/** @jsxImportSource preact */
import { UiSwitch } from "@threadlabs/looma";
export default function Example() {
return <form>
<UiSwitch
name="alerts"
value="enabled"
required
>Email alerts</UiSwitch>
</form>;
}<script>
import { UiSwitch } from "@threadlabs/looma";
</script>
<form>
<UiSwitch
name="alerts"
value="enabled"
required
>Email alerts</UiSwitch>
</form>import { Component } from "@angular/core";
import { UiSwitch } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiSwitch],
template: `
<form>
<label
ui-switch
[name]="'alerts'"
[value]="'enabled'"
[required]="true"
>Email alerts</label>
</form>
`,
})
export default class Example {}With a description
The description slot says what the setting does, on a line under its label. It is the input's accessible description, not part of its name.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-switch/ui-switch.html"
>
<ui-switch
name="reviews"
checked
>
Reviews
<span
slot="description"
>Editors can ask someone to review their changes before they publish.</span>
</ui-switch><script setup>
import { UiSwitch } from "@threadlabs/looma";
</script>
<template>
<UiSwitch
name="reviews"
:checked="true"
>Reviews<template #description>
<span>Editors can ask someone to review their changes before they publish.</span>
</template></UiSwitch>
</template>import { UiSwitch } from "@threadlabs/looma";
export default function Example() {
return <UiSwitch
name="reviews"
checked
slots={{
description: <span>Editors can ask someone to review their changes before they publish.</span>,
}}
>Reviews</UiSwitch>;
}/** @jsxImportSource preact */
import { UiSwitch } from "@threadlabs/looma";
export default function Example() {
return <UiSwitch
name="reviews"
checked
slots={{
description: <span>Editors can ask someone to review their changes before they publish.</span>,
}}
>Reviews</UiSwitch>;
}<script>
import { UiSwitch } from "@threadlabs/looma";
</script>
{#snippet description()}
<span>Editors can ask someone to review their changes before they publish.</span>
{/snippet}
<UiSwitch
name="reviews"
checked
slots={{ description }}
>Reviews</UiSwitch>import { Component } from "@angular/core";
import { UiSwitch } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiSwitch],
template: `
<label
ui-switch
[name]="'reviews'"
[checked]="true"
>Reviews<ng-container ngProjectAs="[slot='description']">
<span>Editors can ask someone to review their changes before they publish.</span>
</ng-container></label>
`,
})
export default class Example {}size
size="sm" keeps the switch's track and centres its label in the small control height, in the small controls' type size, so it lines up with a small button.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-cluster/ui-cluster.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-switch/ui-switch.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<ui-cluster
gap="s"
align="center"
>
<ui-switch
size="sm"
>Switch</ui-switch>
<ui-button
size="sm"
>Small button</ui-button>
</ui-cluster><script setup>
import { UiButton, UiCluster, UiSwitch } from "@threadlabs/looma";
</script>
<template>
<UiCluster
gap="s"
align="center"
>
<UiSwitch
size="sm"
>Switch</UiSwitch>
<UiButton
size="sm"
>Small button</UiButton>
</UiCluster>
</template>import { UiButton, UiCluster, UiSwitch } from "@threadlabs/looma";
export default function Example() {
return <UiCluster
gap="s"
align="center"
>
<UiSwitch
size="sm"
>Switch</UiSwitch>
<UiButton
size="sm"
>Small button</UiButton>
</UiCluster>;
}/** @jsxImportSource preact */
import { UiButton, UiCluster, UiSwitch } from "@threadlabs/looma";
export default function Example() {
return <UiCluster
gap="s"
align="center"
>
<UiSwitch
size="sm"
>Switch</UiSwitch>
<UiButton
size="sm"
>Small button</UiButton>
</UiCluster>;
}<script>
import { UiButton, UiCluster, UiSwitch } from "@threadlabs/looma";
</script>
<UiCluster
gap="s"
align="center"
>
<UiSwitch
size="sm"
>Switch</UiSwitch>
<UiButton
size="sm"
>Small button</UiButton>
</UiCluster>import { Component } from "@angular/core";
import { UiButton, UiCluster, UiSwitch } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiCluster, UiSwitch, UiButton],
template: `
<div
ui-cluster
[gap]="'s'"
[align]="'center'"
>
<label
ui-switch
[size]="'sm'"
>Switch</label>
<ng-template
ui-button
[size]="'sm'"
>Small button</ng-template>
</div>
`,
})
export default class Example {}API
Package: @threadlabs/looma · Native root: label
Attributes
| Name | Description | Property | Type | Default | Allowed values |
|---|---|---|---|---|---|
checked | Whether the switch is checked initially or when set externally. A form reset returns it to this. | checked | boolean | false | — |
disabled | Disables the native switch, so it cannot be toggled or focused. | disabled | boolean | false | — |
name | The native name the switch submits under: a form that contains it sends name=value while it is on, and nothing while it is off, as a native checkbox does. Left empty, it takes no part in form submission. | name | string | | — |
required | Marks the native switch required, so native form validation treats it as invalid until it is on. | required | boolean | false | — |
size | Aligns the switch with fields and buttons of the same size. It keeps its track size; the label takes that size's field type size and its first line centres in that control height (sm 2rem, lg 3rem by default), so in a row of inputs, selects, and buttons of one size it shares their height and their text baseline. Touch input keeps the same visible alignment. md is the standard layout. | size | keyword | md | sm, md, lg |
value | The value a form submits under name while the switch is on, also reported in the change event's detail so one handler can tell switches apart. on by default, as on a native checkbox. | value | string | on | — |
Framework props
| Name | Type | Default | Allowed values |
|---|---|---|---|
checked | boolean | false | — |
disabled | boolean | false | — |
name | string | | — |
required | boolean | false | — |
size | "sm" | "md" | "lg" | md | sm, md, lg |
value | string | on | — |
Events
| Name | Detail | Description |
|---|---|---|
change | object
| Emitted when a toggleable control changes checked state. |
Slots
| Name | Purpose |
|---|---|
default | Default child content. |
description | Named content for the description region. |
Design tokens
Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.
Component tokens
| Token | Declared values | Fallback |
|---|---|---|
--ui-switch-gap | — | var(--ui-space-2) |
--ui-switch-track-height | — | var(--ui-choice-track-height) | 1.5rem |
--ui-switch-track-width | — | var(--ui-choice-track-width) | 2.5rem |
Shared tokens
| Token | Fallback |
|---|---|
--ui-accent-highlight | none |
--ui-accent-solid | — |
--ui-border-strong | — |
--ui-border-width | — |
--ui-choice-hover-highlight | none |
--ui-choice-hover-surface | — |
--ui-choice-track-height | — |
--ui-choice-track-width | — |
--ui-control-border | var(--ui-border-strong) |
--ui-control-border-hover | var(--ui-border-strong) |
--ui-control-inset | — |
--ui-control-size-lg | — |
--ui-control-size-sm | — |
--ui-control-surface | var(--ui-surface-default) |
--ui-disabled-border | — |
--ui-disabled-surface | — |
--ui-disabled-text | — |
--ui-focus-ring | var(--ui-accent-solid) |
--ui-focus-width | — |
--ui-font-size-md | — |
--ui-font-size-sm | — |
--ui-knob-highlight | none |
--ui-knob-on-accent-highlight | none |
--ui-line-height | — |
--ui-line-height-md | — |
--ui-motion-base | — |
--ui-motion-ease | — |
--ui-neutral-wash-hover | — |
--ui-radius-round | — |
--ui-shadow-xs | — |
--ui-space-1 | — |
--ui-space-2 | — |
--ui-surface-default | — |
--ui-surface-highlight | none | linear-gradient(transparent, transparent) |
--ui-text-on-accent | — |
--ui-text-primary | — |
--ui-text-secondary | — |