# ButtonGroup

Button group: combines multiple `oas-button` elements into a value-selection group; adjacent button corners merge and hover only highlights the current item.

## Basic usage

<DemoBlock title="Basic button group">
  <oas-button-group>
    <oas-button value="1">January</oas-button>
    <oas-button value="2">February</oas-button>
    <oas-button value="3">March</oas-button>
  </oas-button-group>
</DemoBlock>

## Type & size passthrough

`type` / `size` are passed through uniformly to all child buttons in the group. `size` supports five tiers: `xs` / `small` / `medium` (default) / `large` / `xl`.

<DemoBlock title="Type & size passthrough">
  <oas-button-group type="primary" size="large">
    <oas-button value="a">Left</oas-button>
    <oas-button value="b">Middle</oas-button>
    <oas-button value="c">Right</oas-button>
  </oas-button-group>
  <oas-button-group size="xs" style="margin-top: 8px">
    <oas-button value="a">XS</oas-button>
    <oas-button value="b">XS</oas-button>
    <oas-button value="c">XS</oas-button>
  </oas-button-group>
  <oas-button-group size="xl" style="margin-top: 8px">
    <oas-button value="a">XL</oas-button>
    <oas-button value="b">XL</oas-button>
    <oas-button value="c">XL</oas-button>
  </oas-button-group>
</DemoBlock>

## Variant & round passthrough

`variant` / `round` are passed through uniformly to all child buttons, just like `type` / `size` — set once on the group and every child button applies it. `variant` supports six shapes matching `oas-button`: `solid` (default) / `outlined` / `dashed` / `filled` / `text` / `link`; `round` gives child buttons a capsule rounding.

<DemoBlock title="Variant & round passthrough">
  <oas-button-group variant="outlined">
    <oas-button value="a">Outlined</oas-button>
    <oas-button value="b">Outlined</oas-button>
    <oas-button value="c">Outlined</oas-button>
  </oas-button-group>
  <oas-button-group variant="dashed" type="primary" style="margin-top: 8px">
    <oas-button value="a">Dashed</oas-button>
    <oas-button value="b">Dashed</oas-button>
    <oas-button value="c">Dashed</oas-button>
  </oas-button-group>
  <oas-button-group round style="margin-top: 8px">
    <oas-button value="a">Option A</oas-button>
    <oas-button value="b">Option B</oas-button>
    <oas-button value="c">Option C</oas-button>
  </oas-button-group>
</DemoBlock>

## Single select

Declare the current selection with `value`; clicking dispatches `oas-change` with `detail: { value }`.

<DemoBlock title="Single-select group">
  <oas-button-group value="b" onoas-change="message.info('Selected: ' + event.detail.value)">
    <oas-button value="a">Option A</oas-button>
    <oas-button value="b">Option B</oas-button>
    <oas-button value="c">Option C</oas-button>
  </oas-button-group>
</DemoBlock>

## Multiple select

Add `multiple` to enable multi-select; `value` uses comma-separated selected values. Clicking dispatches `oas-change` with `detail: { value: [] }`.

<DemoBlock title="Multi-select group">
  <oas-button-group multiple value="a,c">
    <oas-button value="a">Tag A</oas-button>
    <oas-button value="b">Tag B</oas-button>
    <oas-button value="c">Tag C</oas-button>
  </oas-button-group>
</DemoBlock>

## Accessible name

`aria-label` gives the button group container an accessible name, letting screen readers announce it as a single group; when unset, the built-in i18n label "button group" is used. When multiple groups exist on a page, use names to distinguish them.

<DemoBlock title="aria-label accessible name">
  <oas-button-group aria-label="View switch" value="list">
    <oas-button value="list">List view</oas-button>
    <oas-button value="grid">Grid view</oas-button>
  </oas-button-group>
  <oas-button-group aria-label="Export results" value="csv">
    <oas-button value="csv">Export CSV</oas-button>
    <oas-button value="pdf">Export PDF</oas-button>
  </oas-button-group>
</DemoBlock>

## Vertical

<DemoBlock title="Vertical button group">
  <oas-button-group vertical>
    <oas-button value="up">Top</oas-button>
    <oas-button value="mid">Middle</oas-button>
    <oas-button value="down">Bottom</oas-button>
  </oas-button-group>
</DemoBlock>

## Spread

`spread` makes child buttons equally share the full container width (common for mobile action bars / form footer button groups). Combine with `type` / `size` / `variant` / `pill`.

<DemoBlock title="Spread">
  <oas-button-group spread type="primary">
    <oas-button value="confirm">Confirm</oas-button>
    <oas-button value="cancel">Cancel</oas-button>
  </oas-button-group>
  <oas-button-group spread pill style="margin-top: 8px">
    <oas-button value="a">Option A</oas-button>
    <oas-button value="b">Option B</oas-button>
    <oas-button value="c">Option C</oas-button>
  </oas-button-group>
  <oas-button-group spread variant="outlined" style="margin-top: 8px">
    <oas-button value="save">Save</oas-button>
    <oas-button value="publish">Publish</oas-button>
    <oas-button value="delete">Delete</oas-button>
  </oas-button-group>
</DemoBlock>

## Disabled & mixed

`disabled` disables the whole group; buttons without a `value` attribute act as regular buttons and don't participate in selection.

<DemoBlock title="Disabled & mixed">
  <oas-button-group disabled>
    <oas-button value="1">Disabled</oas-button>
    <oas-button value="2">Disabled</oas-button>
  </oas-button-group>
  <oas-button-group>
    <oas-button value="save">Save</oas-button>
    <oas-button value="delete" type="danger">Delete</oas-button>
    <oas-button>More actions</oas-button>
  </oas-button-group>
</DemoBlock>

## Pill

The `pill` attribute turns the whole group into a pill: the first/last buttons use `--oas-radius-full` (999px) corners — left rounded on the first / right rounded on the last in a horizontal group, top on the first / bottom on the last in a vertical one. Middle buttons stay square.

<DemoBlock title="Pill">
  <oas-button-group pill value="a" onoas-change="message.info('Selected: ' + event.detail.value)">
    <oas-button value="a">Option A</oas-button>
    <oas-button value="b">Option B</oas-button>
    <oas-button value="c">Option C</oas-button>
  </oas-button-group>
  <oas-button-group pill type="primary" style="margin-top: 8px">
    <oas-button value="prev">Previous</oas-button>
    <oas-button value="next">Next</oas-button>
  </oas-button-group>
  <oas-button-group pill vertical class="bg-eq-pill" style="margin-top: 8px">
    <oas-button value="up">Top</oas-button>
    <oas-button value="mid">Middle</oas-button>
    <oas-button value="down">Bottom</oas-button>
  </oas-button-group>
  <oas-button-group pill vertical class="bg-eq-pill bg-vertical-text" style="margin-top: 8px">
    <oas-button value="up">Top</oas-button>
    <oas-button value="mid">Middle</oas-button>
    <oas-button value="down">Bottom</oas-button>
  </oas-button-group>
</DemoBlock>

<style>
/* Even padding for vertical pills: the inner button is a fixed control height with horizontal-only
   padding, so in a vertical group the top/bottom space ends up much smaller than the sides. Release
   the fixed height and apply one uniform padding so the text has equal margins on all sides.
   Both examples share it: the 3rd (horizontal text) → 46×37 buttons; the 4th (vertical text via
   writing-mode) → 37×46 buttons. */
.bg-eq-pill oas-button::part(button) {
  height: auto;
  min-height: 0;
  padding: var(--oas-space-2);
}
/* Vertical-text variant: upright vertical writing (not rotated), sized by the text plus the same
   uniform padding. The -1px border-merge seam uses the logical margin-block-start; under vertical
   writing its block axis becomes horizontal, so the seam lands on the right and its border gets
   clipped by the group container — realign on the physical top instead. */
.bg-vertical-text oas-button {
  writing-mode: vertical-rl;
}
.bg-vertical-text oas-button:not(:first-child) {
  margin-block-start: 0;
  margin-right: 0;
  margin-top: -1px;
}
/* End buttons' OUTER padding = space-4: the label must not "sink into" the capsule's arc. The arc
   depth equals half the capsule width (23 / 18.5 here), but there's no need to pad the full depth:
   once the label's top row reaches "arc depth - 4", the curved boundary is inset by less than 0.5px
   (visually a straight edge already) — padding the full depth only adds a blank cap.
   Inner side and left/right keep space-2: Top's bottom = left/right, Bottom's top = left/right,
   Middle all four equal.
   Note: under vertical writing (writing-mode: vertical-rl) a percentage padding breaks the host's
   intrinsic height (host doesn't follow the inner button, labels overlap); 50% (= half width) does
   work for the horizontal-writing case, so a fixed value is used here. */
.bg-eq-pill {
  --end-pad: var(--oas-space-4);
}
.bg-eq-pill oas-button:first-child::part(button) {
  padding-top: var(--end-pad);
}
.bg-eq-pill oas-button:last-child::part(button) {
  padding-bottom: var(--end-pad);
}
</style>

## Separator

Put an `<oas-button-group-separator>` inside the group to render a divider line: a 1px vertical line in a horizontal group, a 1px horizontal line in a vertical one (direction follows the group orientation automatically). Separators sit flush between items and don't participate in corner merging — place them between buttons/nested groups.

<DemoBlock title="Separator">
  <oas-button-group>
    <oas-button value="copy">Copy</oas-button>
    <oas-button-group-separator></oas-button-group-separator>
    <oas-button value="cut">Cut</oas-button>
    <oas-button-group-separator></oas-button-group-separator>
    <oas-button value="paste">Paste</oas-button>
  </oas-button-group>
  <oas-button-group vertical style="margin-top: 8px">
    <oas-button value="top">Top</oas-button>
    <oas-button-group-separator></oas-button-group-separator>
    <oas-button value="middle">Middle</oas-button>
    <oas-button-group-separator></oas-button-group-separator>
    <oas-button value="bottom">Bottom</oas-button>
  </oas-button-group>
</DemoBlock>

## Nested groups

An `oas-button-group` can contain another `oas-button-group` as a single whole item, participating in corner merging and flush layout. The outer group passes its start/end corner radii to the nested group via `--oas-button-group-start-radius` / `--oas-button-group-end-radius`. The nested group manages its own buttons (outer `type` / `size` are not passed through); outer `disabled` disables the nested group as a whole.

<DemoBlock title="Nested groups">
  <oas-button-group>
    <oas-button-group>
      <oas-button value="undo">Undo</oas-button>
      <oas-button value="redo">Redo</oas-button>
    </oas-button-group>
    <oas-button-group-separator></oas-button-group-separator>
    <oas-button value="save">Save</oas-button>
    <oas-button-group-separator></oas-button-group-separator>
    <oas-button-group>
      <oas-button value="prev">Prev</oas-button>
      <oas-button value="next">Next</oas-button>
    </oas-button-group>
  </oas-button-group>
</DemoBlock>

## Split Button

Compose a split button with a button-group plus `oas-dropdown split`: the main button dispatches `oas-action` (primary action), the arrow button opens the dropdown menu (menu selection goes through `oas-select`).

<DemoBlock title="Split Button">
  <oas-space size="small">
    <oas-dropdown split onoas-action="message.info('Main action: save')" onoas-select="message.info('Selected: ' + event.detail.value)" items='[{"label":"Save as","value":"save-as"},{"label":"Export PDF","value":"pdf"},{"label":"Delete","value":"delete","disabled":true}]'>
      <oas-button-group>
        <oas-button>Save</oas-button>
      </oas-button-group>
    </oas-dropdown>
    <oas-dropdown split onoas-action="message.info('Main action: publish')" onoas-select="message.info('Selected: ' + event.detail.value)" items='[{"label":"Schedule","value":"scheduled"},{"label":"Save draft","value":"draft"}]'>
      <oas-button-group>
        <oas-button type="primary">Publish</oas-button>
      </oas-button-group>
    </oas-dropdown>
  </oas-space>
</DemoBlock>

## Composition

Compose button groups with existing components such as overlays and inputs.

<DemoBlock title="Buttons + overlays">
  <oas-space size="small">
    <oas-button-group>
      <oas-button value="edit">Edit</oas-button>
      <oas-button value="copy">Copy</oas-button>
    </oas-button-group>
    <oas-dropdown items='[{"label":"Paste as plain text","value":"paste-text"},{"label":"Paste as quote","value":"paste-quote"}]'>
      <oas-button>Paste</oas-button>
    </oas-dropdown>
    <oas-popover title="Format notes" content="Markdown shortcuts are supported; press Enter to send.">
      <oas-button>?</oas-button>
    </oas-popover>
  </oas-space>
</DemoBlock>

<DemoBlock title="Buttons + input">
  <oas-space size="small">
    <oas-input placeholder="Search keywords" style="width: 220px"></oas-input>
    <oas-button-group onoas-change="message.info('Selected: ' + event.detail.value)">
      <oas-button value="search">Search</oas-button>
      <oas-button value="reset">Reset</oas-button>
    </oas-button-group>
  </oas-space>
</DemoBlock>

<DemoBlock title="Toolbar (multiple groups)">
  <oas-flex gap="8px" align="center" style="padding: 8px; border: 1px solid var(--oas-color-border); border-radius: var(--oas-radius-md); background: var(--oas-color-bg)">
    <oas-button-group aria-label="Text format">
      <oas-button value="bold">B</oas-button>
      <oas-button value="italic">I</oas-button>
      <oas-button value="underline">U</oas-button>
    </oas-button-group>
    <oas-button-group aria-label="Alignment">
      <oas-button value="left">Left</oas-button>
      <oas-button value="center">Center</oas-button>
      <oas-button value="right">Right</oas-button>
    </oas-button-group>
    <oas-button-group aria-label="Export format">
      <oas-button value="csv">CSV</oas-button>
      <oas-button value="pdf">PDF</oas-button>
    </oas-button-group>
  </oas-flex>
</DemoBlock>

## API

### oas-button-group

#### Attributes

| Attribute | Description | Type | Default |
| --- | --- | --- | --- |
| `aria-label` | Group container accessible name (defaults to the built-in i18n label) | — | — |
| `disabled` | Disable the whole group | `boolean` | — |
| `multiple` | Multi-select mode | `boolean` | — |
| `pill` | Pill shape: large rounding on the group outer edges (`--oas-radius-full` / `999px`) | — | — |
| `round` | Group-level round passthrough to child buttons (set once, all capsule-rounded) | `boolean` | — |
| `size` | Size passed to child buttons | `string` | — |
| `spread` | Buttons flex:1 to fill the container width evenly (mobile action bars / form footer button groups) | — | — |
| `type` | Type passed to child buttons | `string` | — |
| `value` | Selected value (single value in single-select, comma-separated in multi-select) | `string` | — |
| `variant` | Group-level variant passthrough to child buttons (set once, applies to all) | `string` | — |
| `vertical` | Stack vertically, merging corners top/bottom | `boolean` | — |

#### Events

| Event | Description |
| --- | --- |
| `oas-change` | Selection changed. Single-select `detail: { value }`; multi-select `detail: { value: [] }` |

#### Slots

| Name | Description |
| --- | --- |
| default | Button group members (`oas-button`) |

> Note: child buttons declare their selectable value via the `value` attribute; children without `value` are regular buttons and don't participate in selection or dispatch `oas-change`. The selected state is expressed through the child button's `aria-pressed`; use `oas-button[aria-pressed='true']` to customize the selected style.
