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

Skip to content

Checkbox Group

Checkboxes sharing one value.

Notify me about
<script setup lang="ts">
import { useId } from 'vue'
import { Checkbox } from '@shardsui/vue/checkbox'
import { CheckboxGroup } from '@shardsui/vue/checkbox-group'

const id = useId()

const topics = [
  { value: 'updates', label: 'Updates' },
  { value: 'mentions', label: 'Mentions' },
  { value: 'activity', label: 'Activity' }
]
</script>

<template>
  <CheckboxGroup
    :value="['activity']"
    class="flex flex-col items-start gap-2 text-gray-900"
    :aria-labelledby="id"
  >
    <div class="text-sm font-semibold" :id="id">Notify me about</div>

    <label
      v-for="topic in topics"
      :key="topic.value"
      class="flex items-center gap-2 text-sm font-normal"
    >
      <Checkbox.Root
        name="topics"
        :value="topic.value"
        class="flex size-4 shrink-0 items-center justify-center rounded-xs focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-950 data-checked:bg-gray-900 data-unchecked:border data-unchecked:border-gray-300"
      >
        <Checkbox.Indicator class="flex text-gray-50 data-unchecked:hidden">
          <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" class="size-4">
            <path
              d="M6 14.15L10.0321 18L18 7"
              stroke="currentColor"
              stroke-width="1.5"
              stroke-linecap="round"
              stroke-linejoin="round"
            />
          </svg>
        </Checkbox.Indicator>
      </Checkbox.Root>
      {{ topic.label }}
    </label>
  </CheckboxGroup>
</template>

Anatomy

A Checkbox Group wraps a set of Checkbox parts. Import both and nest the checkboxes inside the group:

<script setup>
import { Checkbox } from '@shardsui/vue/checkbox'
import { CheckboxGroup } from '@shardsui/vue/checkbox-group'
</script>

<template>
  <CheckboxGroup>
    <Checkbox.Root />
  </CheckboxGroup>
</template>

Usage guidelines

  • Form controls must have an accessible name: name them with <label> elements, or with the Field and Fieldset components.

Examples

Labeling a checkbox group

Point the group at a sibling label with aria-labelledby:

<template>
  <div id="protocols-label">Allowed network protocols</div>
  <CheckboxGroup aria-labelledby="protocols-label">
    <!-- checkboxes -->
  </CheckboxGroup>
</template>

For the individual checkboxes, the simplest option is to wrap each one in a <label>:

<template>
  <label>
    <Checkbox.Root value="http" />
    HTTP
  </label>
</template>

Rendering as a native button

Checkbox.Root renders a <span> by default so it can live inside a <label>. When each checkbox has its own label tied to it with for/id, render it as a native button with as="button":

<template>
  <div id="protocols-label">Allowed network protocols</div>
  <CheckboxGroup aria-labelledby="protocols-label">
    <div>
      <label for="protocol-http">HTTP</label>
      <Checkbox.Root id="protocol-http" value="http" as="button">
        <Checkbox.Indicator />
      </Checkbox.Root>
    </div>
  </CheckboxGroup>
</template>

Form integration

Combine Field and Fieldset to label the group and hook it into a form:

<template>
  <Field.Root name="allowedNetworkProtocols">
    <Fieldset.Root>
      <Fieldset.Legend>Allowed network protocols</Fieldset.Legend>
      <CheckboxGroup>
        <label>
          <Checkbox.Root value="http" />
          HTTP
        </label>
        <label>
          <Checkbox.Root value="https" />
          HTTPS
        </label>
        <label>
          <Checkbox.Root value="ssh" />
          SSH
        </label>
      </CheckboxGroup>
    </Fieldset.Root>
  </Field.Root>
</template>

API reference

PropTypeDefault
AttributeDescription
data-disabledPresent when the group is disabled.
data-validPresent when the field is valid (when wrapped in Field.Root).
data-invalidPresent when the field is invalid (when wrapped in Field.Root).
data-touchedPresent when the field has been touched (when wrapped in Field.Root).
data-dirtyPresent when the value has changed from its initial value (when wrapped in Field.Root).
data-filledPresent when at least one checkbox is checked (when wrapped in Field.Root).
data-focusedPresent when the group is focused (when wrapped in Field.Root).