<button-group>

Group of exclusive push buttons

Features

Examples

Basic, no selected option:

<button-group>
	<button>Design</button>
	<button>Preview</button>
</button-group>

Providing values:

<button-group id="temporal" oninput="out.textContent = this.value">
	<button value="">None</button>
	<button value="d">Dates</button>
	<button value="t">Times</button>
	<button value="dt">Dates & Times</button>
</button-group>
<output id="out"></output>

Pre-selected state via aria-pressed:

<button-group>
	<button>Design</button>
	<button aria-pressed="true">Preview</button>
</button-group>

Multiple:

<button-group multiple oninput="button_multiple_value.textContent = this.value">
	<button value="b"><span style="font-weight: bold">B</span></button>
	<button value="i"><span style="font-style: italic">I</span></button>
	<button value="u"><span style="text-decoration: underline">U</span></button>
</button-group>
<output id="button_multiple_value"></output>

Participates in form submission (requires ElementInternals support):

<form action="about:blank" target="_blank">
	<button-group name="favorite_letter">
		<button>A</button>
		<button aria-pressed="true">B</button>
		<button>C</button>
		<button>D</button>
		<button>E</button>
		<button>F</button>
		<button>G</button>
	</button-group>
	<button type=submit>Submit</button>
</form>

Vertical

<button-group name="type" vertical>
	<button value="garlic" aria-pressed="true">Garlic</button>
	<button value="msg">MSG</button>
	<button value="salt">Salt</button>
</button-group>

Separate

<button-group name="type" separate>
	<button>Salt</button>
	<button>Pepper</button>
	<button>Garlic</button>
	<button>Cumin</button>
	<button>Coriander</button>
	<button>Dill</button>
	<button>Parsley</button>
	<button>Turmeric</button>
</button-group>

Dynamically setting element.value:

<button-group id="group1">
	<button>A</button>
	<button aria-pressed="true">B</button>
	<button>C</button>
</button-group>
<button onclick="group1.value = 'C'">Select C</button>

Dynamically adding aria-pressed attribute:

<button-group id="group2">
	<button>A</button>
	<button aria-pressed="true">B</button>
	<button>C</button>
</button-group>
<button onclick="group2.children[2].setAttribute('aria-pressed', 'true')">Select C</button>

Dynamically adding options:

<button-group id="group3">
	<button>1</button>
	<button>2</button>
	<button aria-pressed="true">3</button>
</button-group>
<button onclick="window.counter ||= 3; group3.insertAdjacentHTML('beforeend', `<button aria-pressed=true>${++counter}</button>`)">Add option</button>

WIP: <button-group> has an implicit ARIA Role of region, so adding an aria-label should make it work as a landmark out of the box (requires ElementInternals support):

<button-group aria-label="View switcher">
	<button>Design</button>
	<button aria-pressed="true">Preview</button>
</button-group>

Regular labels should work too (requires ElementInternals support):

<label for="view-switcher">View:</label>
<button-group id="view-switcher">
	<button>Design</button>
	<button aria-pressed="true">Preview</button>
</button-group>

You don't even need to use an actual <button>, custom elements should work too (presentation needs work, but functionality is there):

<style>
sl-button[aria-pressed="true"]::part(base) {
	background: var(--sl-color-primary-100);
	border-color: var(--sl-color-primary-300);
}
</style>
<button-group>
	<sl-button>1</sl-button>
	<sl-button aria-pressed="true">2</sl-button>
	<sl-button>3</sl-button>
</button-group>

Installation

Just include the component's JS file and you're good:

<script src="https://nudeui.com/button-group/button-group.js" type="module"></script>

In case you want to link to local files: CSS is fetched automatically, and assumed to be in the same directory as the JS file.