Skip to content
Kappa
Toggle
@dicehub/kappav0.4.2

Toggle

A two-state button that represents whether an action or display option is active.

<script setup>
import { Toggle } from "@dicehub/kappa/components/toggle";
import { Bookmark } from "@lucide/vue";
</script>

<template>
  <Toggle
    aria-label="Toggle bookmark"
    class="bookmark-toggle"
    size="sm"
    variant="outline"
  >
    <Bookmark aria-hidden="true" class="bookmark-toggle__icon" />
    Bookmark
  </Toggle>
</template>

<style scoped>
.bookmark-toggle[data-state="on"] .bookmark-toggle__icon {
  fill: currentColor;
}
</style>

Installation

Toggle is part of the main Kappa package. It uses a native pressed button and adds no state-machine dependency.

Barrel

import { Toggle } from "@dicehub/kappa";

Granular

import { Toggle } from "@dicehub/kappa/components/toggle";

Usage

Use Toggle for a persistent pressed state, such as bold text or a saved view. Use Button for a one-time action and Switch for a setting that takes effect immediately.

<script setup>
import { Bold } from "@lucide/vue";
import { Toggle } from "@dicehub/kappa/components/toggle";
</script>

<template>
  <Toggle aria-label="Bold" default-pressed>
    <Bold aria-hidden="true" />
  </Toggle>
</template>

Composition

Typical part hierarchy
Toggle <button aria-pressed>
└── icon and/or visible label

Kappa renders a native button with aria-pressed. The browser owns focus and keyboard activation. Kappa owns controlled and uncontrolled state, compact geometry, and the visible on and off states.

The API uses a native button with aria-pressed. Ark UI provides Toggle Group, but it does not provide a standalone Toggle primitive. Wrapping one group item would add an unnecessary element and group keyboard behavior, so Kappa uses the native button pattern.

Kappa uses the size name base to match the rest of the library. Its Vue event ispressedChange, which corresponds to Base UI's ReactonPressedChange callback.

Examples

Variants

Use the default treatment in quiet toolbars. Use outline when the control needs a visible boundary.

<script setup>
import { Bold, Italic } from "@lucide/vue";
import { Toggle } from "@dicehub/kappa/components/toggle";
</script>

<template>
  <Toggle aria-label="Bold" default-pressed>
    <Bold aria-hidden="true" />
  </Toggle>
  <Toggle aria-label="Italic" variant="outline">
    <Italic aria-hidden="true" />
  </Toggle>
</template>

With Text

Keep a visible label when the icon alone may not be clear.

<script setup>
import { Italic } from "@lucide/vue";
import { Toggle } from "@dicehub/kappa/components/toggle";
</script>

<template>
  <Toggle variant="outline">
    <Italic aria-hidden="true" />
    Italic
  </Toggle>
</template>

Sizes

Small, base, and large sizes keep one pressed-state contract.

<script setup>
import { Bookmark } from "@lucide/vue";
import { Toggle } from "@dicehub/kappa/components/toggle";
</script>

<template>
  <Toggle size="sm" variant="outline">
    <Bookmark aria-hidden="true" />
    Small
  </Toggle>
  <Toggle size="base" variant="outline">
    <Bookmark aria-hidden="true" />
    Base
  </Toggle>
  <Toggle size="lg" variant="outline">
    <Bookmark aria-hidden="true" />
    Large
  </Toggle>
</template>

Controlled

Use v-model:pressed when application state must own and report the value.

Saved
<script setup>
import { Bookmark } from "@lucide/vue";
import { ref } from "vue";
import { Toggle } from "@dicehub/kappa/components/toggle";

const saved = ref(true);
</script>

<template>
  <Toggle v-model:pressed="saved" aria-label="Save report" variant="outline">
    <Bookmark :fill="saved ? 'currentColor' : 'none'" aria-hidden="true" />
  </Toggle>
  <output aria-live="polite">{{ saved ? "Saved" : "Not saved" }}</output>
</template>

Disabled

A disabled toggle preserves its visible state and blocks focus and activation.

<script setup>
import { Bold, Italic } from "@lucide/vue";
import { Toggle } from "@dicehub/kappa/components/toggle";
</script>

<template>
  <Toggle disabled aria-label="Bold unavailable">
    <Bold aria-hidden="true" />
  </Toggle>
  <Toggle disabled default-pressed aria-label="Italic enabled and unavailable">
    <Italic aria-hidden="true" />
  </Toggle>
</template>

Right to Left

Logical spacing keeps icon and text order correct in right-to-left content.

<script setup>
import { Bookmark } from "@lucide/vue";
import { Toggle } from "@dicehub/kappa/components/toggle";
</script>

<template>
  <div dir="rtl" lang="ar">
    <Toggle class="bookmark-toggle" variant="outline">
      <Bookmark aria-hidden="true" class="bookmark-toggle__icon" />
      حفظ
    </Toggle>
  </div>
</template>

<style scoped>
.bookmark-toggle[data-state="on"] .bookmark-toggle__icon {
  fill: currentColor;
}
</style>

Accessibility

  • The native button keeps standard focus, Space, Enter, and disabled behavior.
  • aria-pressed exposes the resolved on or off state.
  • Give icon-only toggles an aria-label or aria-labelledby.
  • Use a visible text label when an icon may be unfamiliar.
  • The default type=button prevents accidental form submission.
  • Pressed, disabled, focus-visible, forced-colors, and reduced-motion states remain distinct.

Keyboard Support

KeyBehavior
SpaceToggles the focused button.
EnterToggles the focused button.

API Reference

Toggle

Renders a native pressed button. Native attributes, data attributes, event listeners, and consumer classes pass to the root.

PropTypeDefaultDescription
pressedboolean—Controls the pressed state. Use with v-model:pressed.
defaultPressedbooleanfalseSets the initial state when the component is uncontrolled.
variant"default" | "outline""default"Selects the quiet or bordered visual treatment.
size"sm" | "base" | "lg""base"Selects compact control, icon, spacing, and text geometry.
disabledbooleanfalseBlocks focus and state changes with native button behavior.
type"button" | "submit" | "reset""button"Sets the native button type. The safe default prevents accidental form submission.

Events

EventPayloadDescription
update:pressedbooleanSupports v-model:pressed and controlled state.
pressedChangebooleanReports the requested pressed state after activation.

Slots

SlotDescription
defaultRenders an icon, visible label, or both inside the button.

Data Attributes

AttributeValueDescription
data-slot"toggle"Identifies the root button.
data-state"on" | "off"Exposes the resolved pressed state.
data-pressedpresentAppears while the toggle is pressed.
data-disabledpresentAppears while the native button is disabled.
data-variant"default" | "outline"Exposes the resolved visual treatment.
data-size"sm" | "base" | "lg"Exposes the resolved compact size.

Exports

ExportDescription
ToggleNative two-state pressed button.
ToggleProps / ToggleEmits / ToggleSlotsPublic Vue component, event, and slot contracts.
ToggleVariant / ToggleSize / ToggleTypeSupported visual, size, and native button options.
TOGGLE_VARIANTS / TOGGLE_SIZES / TOGGLE_TYPESRuntime option lists and safe defaults.
TOGGLE_DEFAULT_VARIANT / TOGGLE_DEFAULT_SIZE / TOGGLE_DEFAULT_TYPEPublished defaults for visual, size, and native button options.
isToggle* / resolveToggle*Runtime guards and safe option resolvers.