Button groups

A row of buttons or icon buttons that react to a press together.

Usage

<ButtonGroup aria-label="Playback">
	<IconButton variant="tonal" width="narrow" size="m" title="Previous">
		<Icon>skip_previous</Icon>
	</IconButton>
	<IconButton variant="filled" width="wide" size="m" title="Play">
		<Icon>play_arrow</Icon>
	</IconButton>
	<IconButton variant="tonal" width="narrow" size="m" title="Next">
		<Icon>skip_next</Icon>
	</IconButton>
</ButtonGroup>
A group needs a name of its own, so pass an aria-label that says what the buttons belong to.

Connected

For selecting an option, switching a view or sorting a page. The buttons are toggle buttons, and the group takes over the corners between them. The outer ends keep the round shape of the button, so leave its buttons on shape="round".

<script lang="ts">
	let places = $state([
		{ label: 'Work', icon: 'work', selected: true },
		{ label: 'Restaurant', icon: 'restaurant', selected: false },
		{ label: 'Coffee', icon: 'coffee', selected: false },
		{ label: 'Home', icon: 'home', selected: false },
	])

	const choose = (index: number) => {
		places.forEach((place, i) => (place.selected = i === index))
	}
</script>

<ButtonGroup variant="connected" aria-label="Place">
	{#each places as place, index (place.label)}
		<Button variant="tonal" toggle bind:selected={place.selected} onclick={() => choose(index)}>
			{#snippet start()}
				<Icon>{place.icon}</Icon>
			{/snippet}
			{place.label}
		</Button>
	{/each}
</ButtonGroup>

Multiple choice

<ButtonGroup variant="connected" aria-label="Text style">
	<Button variant="tonal" toggle bind:selected={styles.bold}>
		{#snippet start()}
			<Icon>format_bold</Icon>
		{/snippet}
		Bold
	</Button>
	<Button variant="tonal" toggle bind:selected={styles.italic}>
		{#snippet start()}
			<Icon>format_italic</Icon>
		{/snippet}
		Italic
	</Button>
	<Button variant="tonal" toggle bind:selected={styles.underline}>
		{#snippet start()}
			<Icon>format_underlined</Icon>
		{/snippet}
		Underline
	</Button>
</ButtonGroup>

Sizes

The buttons carry their own size, the group only holds them together.

<ButtonGroup variant="connected" aria-label="Medium alignment">
	<IconButton size="m" variant="tonal" title="Align left"><Icon>format_align_left</Icon></IconButton>
	<IconButton size="m" variant="tonal" title="Align center">
		<Icon>format_align_center</Icon>
	</IconButton>
	<IconButton size="m" variant="tonal" title="Align right">
		<Icon>format_align_right</Icon>
	</IconButton>
</ButtonGroup>

Motion

expandedRatio is the share of its width a pressed button grows by, compressionLimit caps what a single neighbour gives up. Only standard groups change widths, and reduced motion turns the motion off in both variants.

<ButtonGroup expandedRatio={0.5} aria-label="Rating">
	<Button variant="tonal">Bad</Button>
	<Button variant="tonal">Fine</Button>
	<Button variant="tonal">Great</Button>
</ButtonGroup>

Theming

Tokens

TokenDefault value
--np-button-group-space0.75rem, 0.125rem when connected
--np-button-group-inner-corner0.5rem
--np-button-group-pressed-inner-corner0.25rem

The corner tokens only apply to a connected group.

Example

<ButtonGroup
	variant="connected"
	aria-label="Custom button group"
	--np-button-group-space="0.25rem"
	--np-button-group-inner-corner="1rem"
>
	<Button variant="tonal">Start</Button>
	<Button variant="tonal">Middle</Button>
	<Button variant="tonal">End</Button>
</ButtonGroup>

API

Props

AttributeTypeDefaultDescription
variant'standard' | 'connected''standard'A connected group moves its buttons together and shortens the corners between them.
expandedRationumber0.15Share of its width a pressed button grows by. Standard groups only.
compressionLimitnumber24Pixels a single neighbour gives up at most. Standard groups only.
elementbind:HTMLElementundefinedA reference to the root DOM element of the component. This variable is bound using bind:this, allowing direct access to the underlying HTML element for manipulation or querying within the component's logic.
...attributesHTMLAttributes<HTMLDivElement>Every other attribute is passed to the group element, a div.