Beta, expect API change, and bugs. Hit one? Tell us.

Skip to content

Toggle Group

Toggle buttons sharing a selection.

<script setup lang="ts">
import { Toggle } from '@shardsui/vue/toggle'
import { ToggleGroup } from '@shardsui/vue/toggle-group'

const formats = [
  {
    value: 'bold',
    label: 'Bold',
    linecap: 'square',
    paths: [
      'M17.252 8V7.75C17.252 5.54086 15.4611 3.75 13.252 3.75H8.74219C7.08533 3.75 5.74219 5.09315 5.74219 6.75V9C5.74219 10.6569 7.08533 12 8.74219 12H13.252C15.4611 12 17.252 10.2091 17.252 8Z',
      'M5.74219 12V17.25C5.74219 18.9069 7.08533 20.25 8.74219 20.25H12.502',
      'M13 20.25H14.25C16.4591 20.25 18.25 18.4591 18.25 16.25V16C18.25 13.7909 16.4591 12 14.25 12H13'
    ]
  },
  {
    value: 'italic',
    label: 'Italic',
    linecap: 'round',
    paths: [
      'M9.75 3.75L14.5 3.75L19.25 3.75',
      'M14.5 3.75L9.5 20.25',
      'M4.75 20.25H9.5L14.2601 20.25'
    ]
  },
  {
    value: 'underline',
    label: 'Underline',
    linecap: 'round',
    paths: [
      'M5.75 20.75H18.25',
      'M5.75 3.75V12C5.75 15.4518 8.54822 18.25 12 18.25C15.4518 18.25 18.25 15.4518 18.25 12V3.75'
    ]
  }
] as const
</script>

<template>
  <ToggleGroup :value="['bold']" aria-label="Text formatting" class="flex gap-px">
    <Toggle
      v-for="format in formats"
      :key="format.value"
      :value="format.value"
      :aria-label="format.label"
      class="flex size-8 items-center justify-center rounded-md text-gray-600 select-none hover:bg-gray-100 focus-visible:relative focus-visible:z-10 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-gray-950 active:not-data-pressed:bg-gray-100 data-pressed:bg-gray-200 data-pressed:text-gray-900"
    >
      <svg viewBox="0 0 24 24" fill="none" class="size-4" aria-hidden="true">
        <path
          v-for="d in format.paths"
          :key="d"
          :d="d"
          stroke="currentColor"
          stroke-width="1.5"
          :stroke-linecap="format.linecap"
          stroke-linejoin="round"
        />
      </svg>
    </Toggle>
  </ToggleGroup>
</template>

Anatomy

A Toggle Group wraps a set of Toggle parts:

<script setup>
import { Toggle } from '@shardsui/vue/toggle'
import { ToggleGroup } from '@shardsui/vue/toggle-group'
</script>

<template>
  <ToggleGroup>
    <Toggle value="bold" />
  </ToggleGroup>
</template>

Give every toggle a value — those strings are what the group's value array holds.

Examples

Multiple

Set the multiple prop to let more than one toggle stay pressed at once.

<script setup lang="ts">
import { Toggle } from '@shardsui/vue/toggle'
import { ToggleGroup } from '@shardsui/vue/toggle-group'

const formats = [
  {
    value: 'bold',
    label: 'Bold',
    linecap: 'square',
    paths: [
      'M17.252 8V7.75C17.252 5.54086 15.4611 3.75 13.252 3.75H8.74219C7.08533 3.75 5.74219 5.09315 5.74219 6.75V9C5.74219 10.6569 7.08533 12 8.74219 12H13.252C15.4611 12 17.252 10.2091 17.252 8Z',
      'M5.74219 12V17.25C5.74219 18.9069 7.08533 20.25 8.74219 20.25H12.502',
      'M13 20.25H14.25C16.4591 20.25 18.25 18.4591 18.25 16.25V16C18.25 13.7909 16.4591 12 14.25 12H13'
    ]
  },
  {
    value: 'italic',
    label: 'Italic',
    linecap: 'round',
    paths: [
      'M9.75 3.75L14.5 3.75L19.25 3.75',
      'M14.5 3.75L9.5 20.25',
      'M4.75 20.25H9.5L14.2601 20.25'
    ]
  },
  {
    value: 'underline',
    label: 'Underline',
    linecap: 'round',
    paths: [
      'M5.75 20.75H18.25',
      'M5.75 3.75V12C5.75 15.4518 8.54822 18.25 12 18.25C15.4518 18.25 18.25 15.4518 18.25 12V3.75'
    ]
  }
] as const
</script>

<template>
  <ToggleGroup
    multiple
    :value="['bold', 'underline']"
    aria-label="Text formatting"
    class="flex gap-px"
  >
    <Toggle
      v-for="format in formats"
      :key="format.value"
      :value="format.value"
      :aria-label="format.label"
      class="flex size-8 items-center justify-center rounded-md text-gray-600 select-none hover:bg-gray-100 focus-visible:relative focus-visible:z-10 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-gray-950 active:not-data-pressed:bg-gray-100 data-pressed:bg-gray-200 data-pressed:text-gray-900"
    >
      <svg viewBox="0 0 24 24" fill="none" class="size-4" aria-hidden="true">
        <path
          v-for="d in format.paths"
          :key="d"
          :d="d"
          stroke="currentColor"
          stroke-width="1.5"
          :stroke-linecap="format.linecap"
          stroke-linejoin="round"
        />
      </svg>
    </Toggle>
  </ToggleGroup>
</template>

Inside a toolbar

A group nested in a Toolbar joins the toolbar's arrow-key navigation and its single tab stop. The toolbar's orientation and loopFocus apply instead of the group's, and Home / End no longer move focus. Toolbar.Root / Toolbar.Group also cascade their disabled down to the toggles.

<template>
  <Toolbar.Root>
    <ToggleGroup :value="['bold']">
      <Toggle value="bold">Bold</Toggle>
      <Toggle value="italic">Italic</Toggle>
    </ToggleGroup>
    <Toolbar.Separator />
    <Toolbar.Button>Clear</Toolbar.Button>
  </Toolbar.Root>
</template>

API reference

PropTypeDefault
AttributeDescription
data-disabledPresent when the group is disabled.
data-orientationIndicates the orientation of the toggle group.
data-multiplePresent when multiple is set.

Keyboard:

The group is one tab stop: Tab moves into it and out again, and disabled toggles are skipped.

KeyAction
ArrowRight / ArrowLeftNext / previous toggle when horizontal. Swapped in RTL.
ArrowDown / ArrowUpNext / previous toggle when vertical.
Home / EndFirst / last toggle.