CSC Design System next
VueFlavour Vue React Angular TypeScript PrimaryCSC UI ColorsPrimarySecondaryAccentCustom ColorsRedOrangeGreenBluePurplePink
Guides Getting started Customization Data visualization Migration guide Componentsc-accordionc-alertc-autocompletec-badgec-buttonc-button-groupc-cardc-checkboxc-csc-logoc-data-tablec-dividerc-iconc-icon-buttonc-inputc-linkc-listc-loaderc-login-buttonsc-login-cardc-mainc-menuc-messagec-modalc-navigation-buttonc-otp-inputc-pagec-paginationc-popoverc-progress-barc-progress-circlec-radio-groupc-selectc-side-navigationc-sliderc-spinnerc-statusc-stepsc-switchc-tablec-tabsc-tagsc-text-fieldc-toastsc-toolbarc-tooltip

<c-button-group>

A button group is a segmented control: a compact group of choices rendered as plain `c-button` children, where activating a button takes effect immediately (switching a view, picking a billing period). Selection is exclusive by default; set `multiple` to let several buttons be active at once. Give the group a `label` when it acts as a form field so the choice it represents is named for every user.

Usage

When to use

  • A small set (2–5) of short options selected in place.
  • The selection applies immediately — no separate submit step is implied.

When not to use

  • Long or dynamic option lists — use c-select.
  • Options that need explanation per choice — use c-radio-group (or c-checkboxes instead of multiple).
  • Switching between content panels — use c-tabs with c-tab-buttons, which drives this component internally.

Single vs multiple

Without multiple the group holds one value — the active button's value (or its index when no button declares one). Clicking the active button toggles it off (value null) unless mandatory is set.

With multiple the value is an array of the active buttons' values in DOM order. Arrays have no attribute form — bind value as a DOM property (:value.prop in Vue; the React wrapper and property assignment do this naturally).

In both modes every active button paints its own fill. The sliding indicator that glides between choices is a tab-strip affordance — it belongs to c-tab-buttons, not to this component.

Required vs mandatory

The two props answer different questions and vary independently:

  • required is a form-level demand: this field must be answered before the form is submitted. It renders the required marker on the label; your form logic enforces it.
  • mandatory is a selection-behavior rule: the selection can never become empty. The active button — or, with multiple, the last active button — cannot be toggled off. It says nothing about whether the form demands an answer.

A group can be mandatory without being required (a view switcher that always has a selection) or required without being mandatory (the user must answer, but may retract while deciding).

Accessibility

Setting label names the group for assistive technology (role="group" + aria-labelledby) as well as visually. Each button carries aria-pressed for its active state. Buttons are reachable with the arrow keys once the group has focus.

Examples

Basic
Vue React Angular TypeScript
<template>
  <div class="example-row">
    <c-button-group v-model="view">
      <c-button value="day">Day</c-button>

      <c-button value="week">Week</c-button>

      <c-button value="month">Month</c-button>
    </c-button-group>

    <p>Selected: {{ view ?? 'none' }}</p>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const view = ref<string | null>('week');
</script>
Label
Vue React Angular TypeScript
<template>
  <c-button-group label="Billing period" required value="monthly" mandatory>
    <c-button value="monthly">Monthly</c-button>

    <c-button value="yearly">Yearly</c-button>
  </c-button-group>
</template>
Mandatory
Vue React Angular TypeScript
<template>
  <div class="example-row">
    <c-button-group v-model="align" label="Alignment" mandatory>
      <c-button value="left">Left</c-button>

      <c-button value="center">Center</c-button>

      <c-button value="right">Right</c-button>
    </c-button-group>

    <p>Selected: {{ align }} — the active button cannot be toggled off</p>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const align = ref('left');
</script>
Multiple
Vue React Angular TypeScript
<template>
  <div class="example-row">
    <c-button-group v-model="toppings" label="Toppings" multiple>
      <c-button value="cheese">Cheese</c-button>

      <c-button value="pepperoni">Pepperoni</c-button>

      <c-button value="mushroom">Mushroom</c-button>
    </c-button-group>

    <p>Selected: {{ toppings.length ? toppings.join(', ') : 'none' }}</p>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const toppings = ref<string[]>(['cheese']);
</script>
Sizes
Vue React Angular TypeScript
<template>
  <div class="example-row">
    <c-button-group value="list" mandatory>
      <c-button value="list">List</c-button>

      <c-button value="grid">Grid</c-button>
    </c-button-group>

    <c-button-group size="small" value="list" mandatory>
      <c-button value="list">List</c-button>

      <c-button value="grid">Grid</c-button>
    </c-button-group>
  </div>
</template>

API reference

<c-button-group>

A button group is a segmented control: a compact group of choices rendered as plain `c-button` children, where activating a button takes effect immediately (switching a view, picking a billing period). Selection is exclusive by default; set `multiple` to let several buttons be active at once. Give the group a `label` when it acts as a form field so the choice it represents is named for every user.

Properties

PropertyAttributeTypeDefaultDescription
disableddisabledbooleanfalseDisable the whole group — every slotted c-button is disabled and the selection can no longer be changed.
labellabelstring''Label of the button group, shown above the buttons
mandatorymandatorybooleanfalseThe selection can never become empty: the active button (or, with `multiple`, the last active button) cannot be toggled off. Distinct from `required`: mandatory is a selection-behavior rule on the control, not a form-level demand for an answer.
multiplemultiplebooleanfalseAllow several buttons to be active at once. The value becomes an array of the active buttons' values (in DOM order). Arrays have no attribute form — bind `value` as a DOM property (`:value.prop` in Vue).
requiredrequiredbooleanfalseSet as required — shows the required marker on the label
sizesizeCButtonGroupSize'default'Size of the buttons
valueCButtonGroupValuenullValue of the group: the active button's `value` (or its index when no button declares one). `null` when nothing is selected. With `multiple`, an array of the active buttons' values.

Events

EventDetailDescription
changeCButtonGroupValueFired when the user changes the selection, carrying the new value: the activated button's value (or index), `null` when the active button is toggled off, or the array of active values in `multiple` mode.
inputvoidNative bubbling input event dispatched for plain `v-model` support; carries no detail.
update:valueCButtonGroupValueFired alongside `change` with the new selection; fulfills the `v-model` contract.

Slots

SlotDescription
defaultDefault slot for the c-button elements

CSS parts

Style from outside with c-button-group::part(name) — parts are the library's only styling customization API.

PartDescription
rootThe segmented-control box that frames the buttons
labelThe group label rendered above the buttons

Types

Importable from the package root: import type { … } from '@cscfi/csc-ui'

CButtonGroupProps
export interface CButtonGroupProps {
  /** Disable the whole group — every slotted c-button is disabled and the selection can no longer be changed. */
  disabled?: boolean;
  /**
   * Label of the button group, shown above the buttons
   *
   * @freeform
   */
  label?: string;
  /**
   * The selection can never become empty: the active button (or, with
   * `multiple`, the last active button) cannot be toggled off. Distinct from
   * `required`: mandatory is a selection-behavior rule on the control, not a
   * form-level demand for an answer.
   */
  mandatory?: boolean;
  /**
   * Allow several buttons to be active at once. The value becomes an array
   * of the active buttons' values (in DOM order). Arrays have no attribute
   * form — bind `value` as a DOM property (`:value.prop` in Vue).
   */
  multiple?: boolean;
  /**
   * Set as required — shows the required marker on the label
   */
  required?: boolean;
  /**
   * Size of the buttons
   */
  size?: CButtonGroupSize;
  /**
   * Value of the group: the active button's `value` (or its index when no
   * button declares one). `null` when nothing is selected. With `multiple`,
   * an array of the active buttons' values.
   */
  value?: CButtonGroupValue;
}
CButtonGroupSize

Size of the button group. `small` renders a more compact control; the size is also propagated to every slotted `<c-button>`. Omitting the attribute renders the default size.

export type CButtonGroupSize = 'default' | 'small';
CButtonGroupValue

Selection value of the group: a single button value (or index) — `null` when nothing is selected — or, in `multiple` mode, an array of them.

export type CButtonGroupValue = (number | string)[] | null | number | string;