A button group places related buttons in one visual cluster. Use it for commands such as playback controls or for toggle buttons such as text formatting. Each item remains a native button and a Tab stop.
The root is a <div class="button-group">. Common
buttons and
icon buttons can be direct children.
Call Expressive.AutoInit() to keep a standard group's width
stable while the pressed item grows. Add data-selection to
have it manage toggle state too.
<div class="button-group" data-selection="multiple"
role="group" aria-label="Text formatting">
<button type="button" class="button tonal" aria-pressed="false">
<span class="material-symbols" aria-hidden="true">format_bold</span>
Bold
</button>
<button type="button" class="button tonal" aria-pressed="false">
<span class="material-symbols" aria-hidden="true">format_italic</span>
Italic
</button>
<button type="button" class="button tonal" aria-pressed="false">
<span class="material-symbols" aria-hidden="true">format_underlined</span>
Underline
</button>
</div>
Use a plain group for commands. Add selection behavior only when pressing a button toggles an on/off state. When the choice belongs in form data, keep native radios or checkboxes and synchronize the buttons with those inputs in application code.
Standard
Standard is the default. Items keep separate shapes, and the gap closes
as the size grows: 18dp at xsmall, 12dp at
small, and 8dp from medium through
xlarge. Pressing an item widens it by 15% of its measured
width and morphs its corners toward the square shape for that size. The
immediate neighbors give up that width, so the group's outer width stays
fixed.
<div class="button-group" role="group" aria-label="Playback">
<button type="button" class="icon-button filled" aria-label="Previous track">
<span class="material-symbols" aria-hidden="true">skip_previous</span>
</button>
<button type="button" class="icon-button filled" aria-label="Play">
<span class="material-symbols" aria-hidden="true">play_arrow</span>
</button>
<button type="button" class="icon-button filled" aria-label="Next track">
<span class="material-symbols" aria-hidden="true">skip_next</span>
</button>
</div>
Connected
Add connected to a selection group. Do not use the connected
treatment for one-shot actions. The group fills its container with equal
items separated by 2dp. The outer ends stay round and the joins are
square. The live M3 measurement keeps the xsmall join at
4dp. Pressing small and medium changes their
inner corners from 8dp to 4dp. The larger pairs are 16dp to 12dp at
large and 20dp to 16dp at xlarge.
<div class="button-group connected"
data-selection="single"
data-selection-required
role="group"
aria-label="Text alignment">
<button type="button" class="icon-button tonal"
aria-label="Align left" aria-pressed="true">
<span class="material-symbols" aria-hidden="true">format_align_left</span>
</button>
<button type="button" class="icon-button tonal"
aria-label="Align center" aria-pressed="false">
<span class="material-symbols" aria-hidden="true">format_align_center</span>
</button>
<button type="button" class="icon-button tonal"
aria-label="Align right" aria-pressed="false">
<span class="material-symbols" aria-hidden="true">format_align_right</span>
</button>
</div>
Selection
A selection group contains toggle buttons. Add
data-selection="single" when at most one item may be on, or
data-selection="multiple" for independent toggles. Add
data-selection-required when at least one must remain on.
Give every item its initial aria-pressed value. After
AutoInit(), ButtonGroup keeps it current.
Connected items stay equal in width. If translated labels that do not
wrap need more room than the containing block, the group expands. To
keep every label and keyboard focus ring reachable on narrow screens,
place the group in an overflow-x: auto; padding: 5px
container.
<div class="button-group connected"
data-selection="multiple"
role="group"
aria-label="Text formatting">
<button type="button" class="button filled" aria-pressed="true">Bold</button>
<button type="button" class="button filled" aria-pressed="false">Italic</button>
<button type="button" class="button filled" aria-pressed="false">Underline</button>
</div>
<script>
Expressive.AutoInit();
</script>
Selection changes color, shape, and icon fill, so color is not the only
cue. Native buttons handle Enter and Space, and all items stay in the
Tab order. Keep visible labels when space allows. Give an unfamiliar
icon-only item a tooltip as well as an aria-label.
Sizes
Put xsmall, small (the default),
medium, large, or xlarge on the
group. Common and icon buttons inherit the matching 32dp, 40dp, 56dp,
96dp, or 136dp geometry. An explicit size on a child still wins. The
32dp and 40dp visual sizes keep a minimum 48dp interaction target.
<div class="button-group medium">
<button type="button" class="button tonal">One</button>
<button type="button" class="button tonal">Two</button>
<button type="button" class="button tonal">Three</button>
</div>
Semantics
A button group is not a composite widget. Do not use
role="toolbar", radiogroup, or a roving
tabindex. Users reach items with Tab. When the cluster needs
a name, use role="group" with aria-label or
aria-labelledby. This role adds no arrow-key contract.
Action groups accept a direct <button> or
<a class="button" href>. Selection groups accept only
direct <button type="button"> items, each with an
initial aria-pressed. Links cannot expose toggle state. The
native button supplies keyboard activation, and ButtonGroup
changes the state on the resulting click.
Give every icon-only item an accessible name because its decorative icon
is aria-hidden. Avoid a standard icon button when its meaning
is not familiar. Use a filled, tonal, or outlined container and a tooltip.
Reduced-motion preferences remove the width and shape transitions.
Tokens
Set these properties on the group. The defaults describe a small standard group. Size and variant classes replace the values that differ.
| Token | Default |
|---|---|
--md-comp-button-group-between-space | 12px |
--md-comp-button-group-pressed-item-width-multiplier | .15 |
--md-comp-button-group-item-shape-round | 9999px |
--md-comp-button-group-item-shape-square | 12px |
--md-comp-button-group-pressed-item-shape | 8px |
--md-comp-button-group-container-shape | Half the item height (connected) |
--md-comp-button-group-inner-corner-corner-size | 8px (connected) |
--md-comp-button-group-pressed-inner-corner-corner-size | 4px (connected) |
--md-comp-button-group-selected-inner-corner-corner-size | Half the item height (connected) |
Child geometry and colors still come from the button or icon-button token
family. Toggle groups map the selected and unselected Material button
colors onto aria-pressed and keep hover, focus, pressed,
disabled, and focus-ring feedback.