<script setup>
import { ref } from "vue";
import { Switch } from "@dicehub/kappa/components/switch";
const airplaneMode = ref(false);
</script>
<template>
<Switch.Root v-model:checked="airplaneMode">
<Switch.Control />
<Switch.Label>Airplane mode</Switch.Label>
</Switch.Root>
</template>Installation
Switch is part of the main Kappa package and uses the Ark UI Vue switch primitive.
Barrel
import {
Switch,
SwitchRoot,
SwitchRootProvider,
SwitchControl,
SwitchThumb,
SwitchLabel,
SwitchContext,
} from "@dicehub/kappa";Granular
import {
Switch,
SwitchRoot,
SwitchRootProvider,
SwitchControl,
SwitchThumb,
SwitchLabel,
SwitchContext,
} from "@dicehub/kappa/components/switch";Usage
Use Switch for a setting that changes immediately. Use Checkbox when the user confirms a selection later with a form action. Root renders the native hidden input automatically.
<script setup>
import { Switch } from "@dicehub/kappa/components/switch";
</script>
<template>
<Switch.Root default-checked name="automatic-updates" value="enabled">
<Switch.Control />
<Switch.Label>Automatic updates</Switch.Label>
</Switch.Root>
</template>Composition
Switch.Root
├── Switch.Control
│ └── Switch.Thumb
├── Switch.Label
└── HiddenInput (automatic)Ark UI Switch owns binary state, keyboard behavior, focus, label activation, and the form input. Kappa adds the compound exports, automatic hidden input, default thumb, three compact sizes, and semantic styling.
Kappa uses Ark parts and native fieldsets instead of a custom switch-group state layer.
<script setup>
import {
SwitchRoot,
SwitchControl,
SwitchThumb,
SwitchLabel,
} from "@dicehub/kappa/components/switch";
</script>
<template>
<SwitchRoot>
<SwitchControl>
<SwitchThumb />
</SwitchControl>
<SwitchLabel>Automatic updates</SwitchLabel>
</SwitchRoot>
</template>Examples
With Description
Put persistent guidance beside the switch when the setting needs more context.
<script setup>
import { Switch } from "@dicehub/kappa/components/switch";
</script>
<template>
<Switch.Root class="setting" default-checked>
<Switch.Label class="setting__copy">
<span class="setting__title">Share across devices</span>
<span class="setting__description">
Keep preferences synchronized on signed-in devices.
</span>
</Switch.Label>
<Switch.Control />
</Switch.Root>
</template>
<style scoped>
.setting {
width: 100%;
justify-content: space-between;
align-items: flex-start;
}
.setting__copy {
display: grid;
gap: 0.125rem;
}
.setting__title {
font-weight: 600;
}
.setting__description {
color: var(--kappa-subtle, #6c7480);
font-size: 0.75rem;
font-weight: 400;
}
</style>Choice Card
Because Root renders a label, the complete bordered row remains one clickable target.
<script setup>
import { Switch } from "@dicehub/kappa/components/switch";
</script>
<template>
<Switch.Root class="choice-card" default-checked>
<Switch.Label class="choice-card__copy">
<span class="choice-card__title">Enable notifications</span>
<span class="choice-card__description">
Receive a message when important activity needs attention.
</span>
</Switch.Label>
<Switch.Control />
</Switch.Root>
</template>
<style scoped>
.choice-card {
width: 100%;
align-items: flex-start;
justify-content: space-between;
padding: 0.875rem;
border: 1px solid var(--kappa-line, #e3e6eb);
border-radius: 0.625rem;
}
.choice-card:has([data-state="checked"]) {
border-color: var(--kappa-accent, #4356e8);
}
.choice-card__copy {
display: grid;
gap: 0.25rem;
}
.choice-card__title {
font-weight: 600;
}
.choice-card__description {
color: var(--kappa-subtle, #6c7480);
font-size: 0.75rem;
font-weight: 400;
}
</style>Controlled
Use v-model:checked when application state must own and report the value.
<script setup>
import { ref } from "vue";
import { Switch } from "@dicehub/kappa/components/switch";
const focusMode = ref(true);
</script>
<template>
<Switch.Root v-model:checked="focusMode">
<Switch.Control />
<Switch.Label>Focus mode</Switch.Label>
</Switch.Root>
<output aria-live="polite">State: {{ focusMode ? "on" : "off" }}</output>
</template>State from Context
Read the current state inside the component without creating a second source of truth.
<script setup>
import { Switch } from "@dicehub/kappa/components/switch";
</script>
<template>
<Switch.Root default-checked>
<Switch.Control />
<Switch.Label>
Wi-Fi
<Switch.Context v-slot="{ checked }">
<span>{{ checked ? "On" : "Off" }}</span>
</Switch.Context>
</Switch.Label>
</Switch.Root>
</template>States
Disabled, read-only, and invalid states keep distinct behavior and visible treatment.
<script setup>
import { Field } from "@dicehub/kappa/components/field";
import { Switch } from "@dicehub/kappa/components/switch";
</script>
<template>
<Switch.Root disabled>
<Switch.Control />
<Switch.Label>Disabled</Switch.Label>
</Switch.Root>
<Switch.Root disabled default-checked>
<Switch.Control />
<Switch.Label>Disabled and on</Switch.Label>
</Switch.Root>
<Switch.Root read-only default-checked>
<Switch.Control />
<Switch.Label>Read-only</Switch.Label>
</Switch.Root>
<Field.Root id="terms" invalid>
<Switch.Root invalid required>
<Switch.Control />
<Switch.Label>Accept the terms</Switch.Label>
</Switch.Root>
<Field.ErrorText>You must accept the terms to continue.</Field.ErrorText>
</Field.Root>
</template>Sizes
Use small, base, or large geometry without changing the interaction contract.
<script setup>
import { Switch } from "@dicehub/kappa/components/switch";
</script>
<template>
<Switch.Root size="sm" default-checked>
<Switch.Control />
<Switch.Label>Small</Switch.Label>
</Switch.Root>
<Switch.Root size="base" default-checked>
<Switch.Control />
<Switch.Label>Base</Switch.Label>
</Switch.Root>
<Switch.Root size="lg" default-checked>
<Switch.Control />
<Switch.Label>Large</Switch.Label>
</Switch.Root>
</template>Native Form
The automatic hidden input submits name and value through native FormData.
<script setup>
import { ref } from "vue";
import { Button } from "@dicehub/kappa/components/button";
import { Switch } from "@dicehub/kappa/components/switch";
const result = ref("Submit the form to inspect its value.");
const handleSubmit = (event) => {
const data = new FormData(event.currentTarget);
result.value = data.has("product-updates")
? "Product updates enabled"
: "Product updates disabled";
};
</script>
<template>
<form @submit.prevent="handleSubmit">
<Switch.Root name="product-updates" value="enabled" default-checked>
<Switch.Control />
<Switch.Label>Product updates</Switch.Label>
</Switch.Root>
<Button type="submit">Save preferences</Button>
<output aria-live="polite">{{ result }}</output>
</form>
</template>Right to Left
Logical thumb positioning mirrors the checked state in right-to-left layouts.
<script setup>
import { Switch } from "@dicehub/kappa/components/switch";
</script>
<template>
<div dir="rtl" lang="ar">
<Switch.Root dir="rtl" default-checked>
<Switch.Control />
<Switch.Label>تفعيل الإشعارات</Switch.Label>
</Switch.Root>
</div>
</template>Accessibility
- Every switch needs a visible
Switch.Labelor an equivalent accessible name. - The hidden checkbox input preserves native focus, form submission, reset, and validation behavior.
- Use Switch only for immediate binary settings. Use Checkbox for agreement or batch selection.
- Pair invalid state with clear error text. Field supplies the hidden input’s
aria-errormessage. - Do not use “on” and “off” as the label. Name the setting that changes.
- Keep checked and unchecked meaning stable. Do not reverse the label after activation.
- Motion follows reduced-motion preferences, and forced-colors mode keeps track geometry visible.
Keyboard Support
| Key | Action |
|---|---|
| Space | Toggles the focused switch. |
API Reference
Switch.Root
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | — | Controls the checked state. |
defaultChecked | boolean | false | Sets the initial uncontrolled state. |
size | "sm" | "base" | "lg" | "base" | Selects compact track, thumb, gap, and type geometry. |
dir | "ltr" | "rtl" | inherited Ark locale | Overrides the locale direction used by Ark UI and logical thumb movement. |
disabled | boolean | false | Blocks focus and state changes. |
readOnly | boolean | false | Keeps the value available but blocks changes. |
invalid | boolean | false | Marks the control invalid and applies the danger treatment. |
required | boolean | false | Marks the hidden native input as required. |
name / value / form | string | — | Configures native form submission and external form association. |
id / ids | string / object | generated | Sets the machine or individual part identifiers. |
label | string | — | Supplies the localized accessible state label used by Ark UI. |
asChild | boolean | false | Merges root behavior onto one direct label child. |
Parts
| Part | Element | Description |
|---|---|---|
Switch.Root | label | Owns state, interaction, size, and the automatic hidden input. |
Switch.Control | span | Renders the focusable visual track and a default Thumb. |
Switch.Thumb | span | Moves between logical inline edges as state changes. |
Switch.Label | span | Names the switch and expands the clickable target. |
Switch.Context | — | Exposes checked, disabled, focused, setChecked, and toggleChecked. |
Switch.RootProvider | label | Uses a machine returned by useSwitch instead of creating one. |
Events
| Event | Payload | Description |
|---|---|---|
update:checked | boolean | Supports v-model:checked and controlled state. |
checkedChange | SwitchCheckedChangeDetails | Reports the complete Ark state-change details. |
Data Attributes
| Attribute | Value | Description |
|---|---|---|
data-state | "checked" | "unchecked" | Exposes the current binary state on Ark parts. |
data-size | "sm" | "base" | "lg" | Exposes the resolved Kappa size on Root. |
data-focus-visible | present | Marks keyboard-visible focus on Control. |
data-hover / data-active | present | Marks pointer hover and active press states. |
data-disabled / data-readonly | present | Marks non-editable states. |
data-invalid / data-required | present | Marks form-validation states. |
Exports
| Export | Description |
|---|---|
Switch | Compound root with Root, RootProvider, Control, Thumb, Label, and Context. |
SwitchRoot / SwitchRootProvider | Named machine-owning and externally provided roots. |
SwitchControl / SwitchThumb / SwitchLabel / SwitchContext | Named composition parts. |
SwitchProps / SwitchEmits / SwitchSize / SwitchDirection | Public Vue, size, and direction contracts. |
SWITCH_SIZES / SWITCH_DEFAULT_SIZE | Supported compact sizes and default. |
isSwitchSize / resolveSwitchSize | Size guard and safe resolver. |
switchAnatomy / useSwitch / useSwitchContext | Re-exported Ark UI anatomy and hooks. |