c2-button-group attaches related c2-button or c2-icon-button elements into one control. The default joined appearance shares borders and outside corners. Use appearance="segmented" for an inset track with an animated single-selection surface; it selects the first enabled item when value is omitted and supports arrow, Home and End keys. Both appearances support orientation="vertical"; vertical selectable groups use Up/Down while horizontal groups use Left/Right. Set selection="single" or selection="multiple" explicitly for selectable joined groups.
For a main action joined to a caret that opens a menu of alternatives (“Send ▾”), use c2-split-button instead of combining a group with a c2-menu.
Import
import '@c2n/components/button-group'
The examples use icons from the separate @c2n/feather-icons package: npm install @c2n/feather-icons.
Importing @c2n/components/button-group also registers c2-button and c2-icon-button.
Usage
Zoom in
Zoom out
ResetS
M
L
Importimport '@c2n/components/button-group'
import '@c2n/components/button'
import '@c2n/components/icon-button'
import '@c2n/components/select'
import '@c2n/components/list-item'
import '@c2n/feather-icons/icons/bold.js'
import '@c2n/feather-icons/icons/italic.js'
import '@c2n/feather-icons/icons/underline.js'
import '@c2n/feather-icons/icons/align-left.js'
import '@c2n/feather-icons/icons/align-center.js'
import '@c2n/feather-icons/icons/align-right.js'
Markup<style>
.outlined {
--c2-button-group--border: 1px solid var(--c2-theme--color-outline, #d4d4d8);
--c2-button-group__divider--color: var(--c2-theme--color-outline, #d4d4d8);
--c2-button__container--background-color: transparent;
--c2-button__container--color: color-mix(in srgb, var(--c2-theme--color-on-surface, #18181b) 82%, var(--c2-theme--color-surface, #ffffff));
--c2-button__container__hover--background-color: var(--c2-theme--color-surface-container, #f4f4f5);
--c2-button__container__hover--color: var(--c2-theme--color-on-surface, #18181b);
--c2-button__container__active--background-color: var(--c2-theme--color-outline-variant, #e4e4e7);
--c2-button__container__selected--background-color: var(--c2-theme--color-primary-container, #edf1fe);
--c2-button__container__selected--color: var(--c2-theme--color-primary, #0265dc);
--c2-icon-button--background-color: transparent;
--c2-icon-button__hover--background-color: var(--c2-theme--color-surface-container, #f4f4f5);
--c2-icon-button__selected--background-color: var(--c2-theme--color-primary-container, #edf1fe);
--c2-icon-button__icon__selected--color: var(--c2-theme--color-primary, #0265dc);
--c2-icon-button__state-layer--size: 36px;
--c2-icon-button__icon--width: 18px;
--c2-icon-button__icon--height: 18px;
--c2-icon-button__icon--color: color-mix(in srgb, var(--c2-theme--color-on-surface, #18181b) 82%, var(--c2-theme--color-surface, #ffffff));
}
.full {
width: 320px;
max-width: 100%;
}
.vertical-segmented {
--c2-button-group__segmented--padding: 4px;
--c2-button-group__segmented--gap: 4px;
--c2-button-group__segmented--background-color: var(--c2-theme--color-outline-variant, #e4e4e7);
--c2-button-group__indicator--border: 1px solid var(--c2-theme--color-outline, #d4d4d8);
--c2-button-group__indicator--box-shadow: 0 2px 6px rgba(24, 24, 27, 0.18);
--c2-button-group__indicator--transition-duration: 320ms;
}
</style>
<div data-label="Attached">
<c2-button-group client:only="lit" aria-label="Document actions">
<c2-button>Save</c2-button>
<c2-button>Publish</c2-button>
<c2-button>Archive</c2-button>
</c2-button-group>
</div>
<div data-label="Outlined">
<c2-button-group client:only="lit" class="outlined" aria-label="Document actions">
<c2-button>Save</c2-button>
<c2-button>Publish</c2-button>
<c2-button>Archive</c2-button>
</c2-button-group>
</div>
<div data-label="Single selection">
<c2-button-group client:only="lit" class="outlined" selection="single" value="week" aria-label="Period">
<c2-button value="day">Day</c2-button>
<c2-button value="week">Week</c2-button>
<c2-button value="month">Month</c2-button>
<c2-button value="year">Year</c2-button>
</c2-button-group>
</div>
<div data-label="Segmented">
<c2-button-group client:only="lit" appearance="segmented" value="list" aria-label="View">
<c2-button value="list">List</c2-button>
<c2-button value="board">Board</c2-button>
<c2-button value="calendar">Calendar</c2-button>
</c2-button-group>
</div>
<div data-label="Segmented sizes">
<c2-button-group client:only="lit" appearance="segmented" size="s" value="day" aria-label="Small period">
<c2-button value="day">Day</c2-button><c2-button value="week">Week</c2-button>
</c2-button-group>
<c2-button-group client:only="lit" appearance="segmented" size="l" value="week" aria-label="Large period">
<c2-button value="day">Day</c2-button><c2-button value="week">Week</c2-button>
</c2-button-group>
</div>
<div data-label="Multiple selection, icon buttons">
<c2-button-group client:load class="outlined" selection="multiple" value="bold" aria-label="Text style">
<c2-icon-button value="bold" aria-label="Bold"><c2-feather-bold></c2-feather-bold></c2-icon-button>
<c2-icon-button value="italic" aria-label="Italic"><c2-feather-italic></c2-feather-italic></c2-icon-button>
<c2-icon-button value="underline" aria-label="Underline"><c2-feather-underline></c2-feather-underline></c2-icon-button>
</c2-button-group>
<c2-button-group client:load class="outlined" selection="single" value="left" aria-label="Alignment">
<c2-icon-button value="left" aria-label="Align left"><c2-feather-align-left></c2-feather-align-left></c2-icon-button>
<c2-icon-button value="center" aria-label="Align center"><c2-feather-align-center></c2-feather-align-center></c2-icon-button>
<c2-icon-button value="right" aria-label="Align right"><c2-feather-align-right></c2-feather-align-right></c2-icon-button>
</c2-button-group>
</div>
<div data-label="Vertical and separated">
<c2-button-group client:only="lit" class="outlined" orientation="vertical" aria-label="Zoom">
<c2-button>Zoom in</c2-button>
<c2-button>Zoom out</c2-button>
<c2-button>Reset</c2-button>
</c2-button-group>
<c2-button-group client:only="lit" class="outlined" selection="single" value="m" style="--c2-button-group--gap: 8px; --c2-button-group--border-radius: 999px" aria-label="Size">
<c2-button value="s">S</c2-button>
<c2-button value="m">M</c2-button>
<c2-button value="l">L</c2-button>
</c2-button-group>
</div>
<div data-label="Vertical segmented">
<c2-button-group client:only="lit" class="vertical-segmented" appearance="segmented" orientation="vertical" size="m" value="list" aria-label="View layout">
<c2-icon-button value="list" aria-label="List view">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M8 6h13M8 12h13M8 18h13"></path><path d="M3 6h.01M3 12h.01M3 18h.01"></path></svg>
</c2-icon-button>
<c2-icon-button value="grid" aria-label="Grid view">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="3" y="3" width="7" height="7" rx="1"></rect><rect x="14" y="3" width="7" height="7" rx="1"></rect><rect x="3" y="14" width="7" height="7" rx="1"></rect><rect x="14" y="14" width="7" height="7" rx="1"></rect></svg>
</c2-icon-button>
</c2-button-group>
</div>
<div data-label="Toolbar">
<c2-button-group client:only="lit" class="outlined" toolbar selection="multiple" value="bold" style="--c2-button-group--gap: 6px" aria-label="Formatting">
<c2-icon-button value="bold" aria-label="Bold">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4h8a4 4 0 0 1 0 8H6zM6 12h9a4 4 0 0 1 0 8H6z"></path></svg>
</c2-icon-button>
<c2-icon-button value="italic" aria-label="Italic">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M19 4h-9M14 20H5M15 4 9 20"></path></svg>
</c2-icon-button>
<c2-icon-button value="underline" aria-label="Underline">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 3v7a6 6 0 0 0 12 0V3M4 21h16"></path></svg>
</c2-icon-button>
<c2-select aria-label="Text size" value="body" style="width: 120px">
<c2-list-item value="body">Body</c2-list-item>
<c2-list-item value="heading">Heading</c2-list-item>
<c2-list-item value="caption">Caption</c2-list-item>
</c2-select>
</c2-button-group>
</div>
<div data-label="Full width and disabled">
<c2-button-group client:only="lit" class="full" appearance="segmented" stretched value="no" aria-label="Answer">
<c2-button value="yes">Yes</c2-button>
<c2-button value="no">No</c2-button>
<c2-button value="maybe">Maybe</c2-button>
</c2-button-group>
<c2-button-group client:only="lit" class="outlined" disabled aria-label="Disabled">
<c2-button>One</c2-button>
<c2-button>Two</c2-button>
</c2-button-group>
</div>
Items identify themselves with a value attribute (their index otherwise). In single mode the group’s value is the pressed item’s value and clicking the pressed item keeps it pressed; in multiple mode value is a comma-separated list and each click toggles one entry. Set value from code to change the selection silently; input and then change fire for pointer and keyboard selection, with the new value in change’s detail.value. The group marks its items toggle in selection modes so unpressed buttons still announce aria-pressed="false", and disabled on the group disables every item without touching the ones you disabled yourself.
The group owns the frame of its items through the button variables: --c2-button-group--border applies one border to every item (an outlined group), --c2-button-group--border-radius rounds the outside corners, and attached items overlap by --c2-button-group__divider--width so shared borders are drawn once. In segmented appearance, size="s|m|l" controls the item dimensions and stretched makes all items share the available width. The track, item and animated indicator each have dedicated --c2-button-group__segmented-* and --c2-button-group__indicator-* theme variables.
A single-selection group (including every segmented one) is one Tab stop, like a radio group: Tab lands on the pressed item, and the arrow keys move the selection, Home and End jump to the ends, and disabled items are skipped. A plain or multiple group keeps one Tab stop per item.
Add toolbar for a row of controls that belongs together: a formatting bar, a table’s actions. The group takes the toolbar role and stays one Tab stop, remembering the item focused last, and the arrow keys, Home and End move focus across every focusable child — c2-button, c2-icon-button, c2-select, c2-text-field, links — without changing the selection, which Space and Enter still do. A key the focused control uses itself is left to it: Up and Down open a c2-select, and inside a text field the arrows and Home/End move the caret, so put a text field last in its toolbar. toolbar combines with any selection and either orientation.
With a name, the group is a form control: it submits its pressed values under that name (one entry per value with selection="multiple", nothing when none is pressed), goes back to its initial value when the form resets, and is disabled by a disabled <fieldset>. It fires input just before change, so v-model and Angular’s ngModel bind to it like any other control.
<form>
<c2-button-group name="view" appearance="segmented" value="list" aria-label="View">
<c2-button value="list">List</c2-button>
<c2-button value="board">Board</c2-button>
</c2-button-group>
</form>