Toggle
Accessible, unstyled toggle button with controlled and uncontrolled state.
| Package | @vizejs/ui/toggle |
| Maturity | stable |
| Own the source | vize lib pull toggle |
| Requires | controllable-state |
| Aliases | toggle button, pressed button, on off button |
| Covers | WAI-ARIA toggle button, React Aria ToggleButton, Reka UI Toggle |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { Toggle } from "@vizejs/ui/toggle";
Or copy the source into your project with vize lib pull toggle (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Controlled bold-formatting toggle bound with v-model. -->
<script setup lang="ts">
import { ref } from "vue";
import { Toggle } from "@vizejs/ui/toggle";
const bold = ref(false);
</script>
<template>
<div>
<Toggle v-model="bold" aria-label="Bold">
<template #default="{ pressed }">
<strong>B</strong>
<span>{{ pressed ? "on" : "off" }}</span>
</template>
</Toggle>
<output>{{ bold ? "Bold text enabled" : "Bold text disabled" }}</output>
</div>
</template>
API
Toggle
Source: src/families/selection/toggle/toggle-button.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
as |
PrimitiveAs |
"button" |
Native element, custom element, or component to render. |
native |
boolean |
true when |
Whether the rendered target already implements native button semantics. |
type |
"button" | "reset" | "submit" |
"button" |
Native button submission behavior. |
modelValue |
boolean |
undefined |
Controlled pressed value. undefined selects uncontrolled behavior. |
defaultPressed |
boolean |
false |
Initial pressed state for uncontrolled use. |
disabled |
boolean |
false |
Remove the control from activation and sequential keyboard focus. |
ariaLabel |
string |
undefined |
Accessible name when no visible label supplies one. |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue |
[value: boolean] |
Fired when the pressed state requests a new controlled boolean value. |
change |
[value: boolean, nativeEvent: MouseEvent] |
Fired after user interaction requests the next pressed value. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
ToggleSlotState |
Renders the toggle contents with current pressed and availability state. |
Exposed
| Member | Type | Description |
|---|---|---|
pressed |
ComputedRef<boolean> |
Current controlled or uncontrolled pressed state. |
focus |
(options?: FocusOptions) => void |
Move focus to the rendered control when supported. |
setPressed |
(value: boolean) => boolean |
Request a pressed-state update and report whether it differs. |
reset |
() => boolean |
Restore the current default pressed state and report whether it changed. |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
Behavior
Normative state × input → outcome table for toggle-button.vue (@vizejs/ui/toggle).
Every row is proven by the named mounted-DOM test in src/families/selection/toggle/toggle.test.ts; a row
without a passing test is a contract violation.
| # | State | Input | Outcome | Proven by |
|---|---|---|---|---|
| T1 | unpressed, native | render | native <button type="button">, accessible name from slot, aria-pressed="false", data-vize-ui="toggle" |
renders a native toggle button with pressed semantics |
| T2 | uncontrolled | pointer click | toggles aria-pressed and data-state; emits update:modelValue then change, in that order |
toggles with pointer activation and emits the requested value |
| T3 | controlled | pointer click | emits the request; the rendered state keeps the parent-provided modelValue until props update |
controlled value wins until the parent accepts the request |
| T4 | uncontrolled, seeded | render / reset | defaultPressed seeds the initial state; exposed reset() restores it |
uncontrolled defaultPressed seeds state and reset restores it |
| T5 | idle, non-native | Enter / Space | non-native rendering exposes role="button" and emulates native button keyboard timing with one requested toggle per activation key |
non-native toggle emulates Enter and Space activation timing |
| T6 | disabled, native | click / Enter / Space | native disabled attribute, no aria-disabled mirror, no toggle, no change, skipped by Tab |
disabled native and non-native toggles suppress activation |
| T7 | disabled, non-native | click / Space / Tab | tabindex="-1", aria-disabled="true", no toggle, no change, skipped by Tab |
disabled native and non-native toggles suppress activation |
| T8 | any | slot / exposed methods | slot receives live pressed and disabled booleans; focus() focuses the control and setPressed() updates uncontrolled pressed state |
exposes focus, setPressed, and slot state |
The subpath remains tree-shakable and retains no packaged CSS; those package
contracts are pinned by distribution.test.ts, check:size, and
check:tree-shaking.