Skip to content

Button group

wu-btn-group joins buttons into one control. The outer corners stay round, inner borders overlap, and the hovered or focused button rises above its neighbors.

Class or attribute Effect
wu-btn-group Root. Inline row of wu-btn children
vertical Stacks the buttons in a column
aria-pressed="true" on a button Toggle state: selected tint plus an inner brand ring
label.wu-btn with a hidden radio or checkbox Checkable button: the checked state takes the same look as aria-pressed
caret button after a button of the same variant Split button with a divider for brand, danger and quiet
animated Spring press scale on each button, opt-in

Give the group a name with role="group" and aria-label.

<div class="wu-btn-group" role="group" aria-label="Text alignment">
<button class="wu-btn" type="button">Left</button>
<button class="wu-btn" type="button">Center</button>
<button class="wu-btn" type="button">Right</button>
</div>

The group takes the size of its buttons.

<div class="flex col gap-3">
<div class="wu-btn-group" role="group" aria-label="Small group">
<button class="wu-btn sm" type="button">Day</button>
<button class="wu-btn sm" type="button">Week</button>
<button class="wu-btn sm" type="button">Month</button>
</div>
<div class="wu-btn-group" role="group" aria-label="Large group">
<button class="wu-btn lg" type="button">Day</button>
<button class="wu-btn lg" type="button">Week</button>
<button class="wu-btn lg" type="button">Month</button>
</div>
</div>

aria-pressed="true" marks the selected button with a tint and an inner ring, so color is not the only signal. Updating the attribute on click is up to your script.

<div class="wu-btn-group" role="group" aria-label="View">
<button class="wu-btn" type="button" aria-pressed="true">List</button>
<button class="wu-btn" type="button" aria-pressed="false">Board</button>
<button class="wu-btn" type="button" aria-pressed="false">Calendar</button>
</div>

A label.wu-btn holding a visually hidden radio. The checked label takes the selected look, arrow keys move between the radios, and keyboard focus draws the ring on the label. For a single choice on a track, see segmented control.

<div class="wu-btn-group" role="radiogroup" aria-label="Alignment">
<label class="wu-btn"><input type="radio" name="btn-group-align" class="sr-only" checked> Left</label>
<label class="wu-btn"><input type="radio" name="btn-group-align" class="sr-only"> Center</label>
<label class="wu-btn"><input type="radio" name="btn-group-align" class="sr-only"> Right</label>
</div>

The same markup with checkboxes allows several selections.

<div class="wu-btn-group" role="group" aria-label="Text style">
<label class="wu-btn"><input type="checkbox" class="sr-only" checked> Bold</label>
<label class="wu-btn"><input type="checkbox" class="sr-only"> Italic</label>
<label class="wu-btn"><input type="checkbox" class="sr-only"> Underline</label>
</div>

A main action plus a caret button that opens a dropdown. A divider separates the two parts.

<div class="flex wrap x-center gap-4">
<div class="wu-btn-group">
<button class="wu-btn brand" type="button">Save</button>
<button class="wu-btn brand caret" type="button" popovertarget="split-brand" aria-label="More save options"></button>
<div class="wu-dropdown inline-end" id="split-brand" popover>
<button class="wu-dropdown-item" type="button" popovertarget="split-brand" popovertargetaction="hide">Save as draft</button>
<button class="wu-dropdown-item" type="button" popovertarget="split-brand" popovertargetaction="hide">Save and close</button>
</div>
</div>
<div class="wu-btn-group">
<button class="wu-btn" type="button">Export</button>
<button class="wu-btn caret" type="button" popovertarget="split-default" aria-label="More export options"></button>
<div class="wu-dropdown inline-end" id="split-default" popover>
<button class="wu-dropdown-item" type="button" popovertarget="split-default" popovertargetaction="hide">Export as CSV</button>
<button class="wu-dropdown-item" type="button" popovertarget="split-default" popovertargetaction="hide">Export as PDF</button>
</div>
</div>
</div>
<div class="wu-btn-group vertical" role="group" aria-label="Order">
<button class="wu-btn" type="button">Move up</button>
<button class="wu-btn" type="button">Move down</button>
<button class="wu-btn" type="button" disabled>Remove</button>
</div>

The corners and overlaps use logical properties and follow the text direction.

<div class="wu-btn-group" role="group" aria-label="الصفحات">
<button class="wu-btn" type="button">واحد</button>
<button class="wu-btn" type="button" aria-pressed="true">اثنان</button>
<button class="wu-btn" type="button">ثلاثة</button>
</div>

animated (or wu-animated on an ancestor) gives every button in the group the button press animation: a small spring scale while pressed. Reduced motion turns it off.

<div class="wu-btn-group animated" role="group" aria-label="Tools">
<button class="wu-btn" type="button">Cut</button>
<button class="wu-btn" type="button">Copy</button>
<button class="wu-btn" type="button">Paste</button>
<button class="wu-btn" type="button">Undo</button>
</div>
@use "@wovenui/components/btn-group";

The button group needs the button. The entry loads both; in the bundle, switching button off while btn-group is on stops the compile with an error.