GitHub

Button Group

Groups buttons with connected borders. Use it purely for visual grouping, or as a radio-like segmented control that emits a composed change event.

Stable wc import '@esa/ecology/esa-button-group';

Preview

<esa-button-group>
  <EsaButton color="primary">Day</EsaButton>
  <EsaButton color="primary">Week</EsaButton>
  <EsaButton color="primary">Month</EsaButton>
</esa-button-group>

Modes

selection-mode="none" is the default — buttons are visually connected but each acts independently.

<esa-button-group selection-mode="none">
  <EsaButton color="primary">Day</EsaButton>
  <EsaButton color="primary">Week</EsaButton>
  <EsaButton color="primary">Month</EsaButton>
</esa-button-group>

selection-mode="single" turns the group into a segmented control. One child stays active; give each child a data-value and listen for the change event.

Selected: week

<esa-button-group selection-mode="single" value="week">
  <button class="seg-btn" data-value="day">Day</button>
  <button class="seg-btn" data-value="week">Week</button>
  <button class="seg-btn" data-value="month">Month</button>
</esa-button-group>

API

PropTypeDefaultDescription
selection-mode 'none' | 'single' 'none' 'none' is visual grouping only; 'single' acts as a radio-like segmented control with one active child.
size 'xs' | 'sm' | 'md' | 'lg' 'md' Reflected sizing hint for the group.
value string '' In 'single' mode, the data-value of the currently selected child.

Events

EventTypeDefaultDescription
change CustomEvent<{ value: string }> Fired in 'single' mode when the active child changes. Composed and bubbling; detail.value is the selected child's data-value (falling back to its text content).

Accessibility

  • The host sets role="group" automatically on connect.
  • In single mode the active child receives aria-pressed="true" and a data-selected attribute for styling; the rest get aria-pressed="false".
  • The change event is composed and bubbles, so it crosses shadow boundaries and can be caught on the host or an ancestor.
  • Slotted children remain real <button> elements — keyboard focus and activation come for free.

Theming surface

Every public token this component reads, extracted from its source at build time. The Resolves to column walks each token's real lineage — component → semantic → primitive → raw value — so you can see exactly where re-pointing it lands. A spoke re-skins by overriding these in its theme-<slug>.css; component internals are never edited. No hook for what you need? File it with /request-lego.

TokenTierResolves to
--form-radius-md component 0.5rem via --radius-200
--color-border semantic #cecece via --color-gray-7