Button Group
<oge-button-group> lays out buttons as one segmented control and optionally tracks selection. Children inherit the group's stylingMode, severity and size; arrow keys move focus with a roving tabindex — in single mode they move the selection too, exactly like a radio group.
Single selection (radio pattern)
One segment stays selected at all times, exactly like a radio group: the group takes role='radiogroup', children become role='radio' with aria-checked, and arrow keys move focus and selection together. selectedKeys is a two-way model, so presets and programmatic changes flow both ways.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeButton, OgeButtonGroup } from '@oge-ui/buttons';
@Component({
selector: 'demo-root',
imports: [OgeButton, OgeButtonGroup],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<oge-button-group
selectionMode="single"
[(selectedKeys)]="align"
ariaLabel="Text alignment"
>
<oge-button value="left" text="Left" />
<oge-button value="center" text="Center" />
<oge-button value="right" text="Right" />
</oge-button-group>
`,
})
export class Demo {
protected readonly align = signal<string[]>(['center']);
}Multiple selection (toggle buttons)
Each segment toggles independently (aria-pressed semantics) — the classic text-formatting toolbar. selectionChanged reports the full state plus addedKeys/removedKeys diffs, so you never have to compute what changed.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeButton, OgeButtonGroup } from '@oge-ui/buttons';
@Component({
selector: 'demo-root',
imports: [OgeButton, OgeButtonGroup],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<oge-button-group
selectionMode="multiple"
[(selectedKeys)]="styles"
stylingMode="outlined"
ariaLabel="Text styles"
>
<oge-button value="bold" text="B" hint="Bold" />
<oge-button value="italic" text="I" hint="Italic" />
<oge-button value="underline" text="U" hint="Underline" />
</oge-button-group>
`,
})
export class Demo {
protected readonly styles = signal<string[]>(['bold']);
}Data-driven items
When the segments come from data, pass an items array instead of (or in addition to) declarative children — each entry carries value, text, optional hint/disabled/severity/badge. The group's stylingMode, severity and size cascade into every child unless the child overrides them.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeButtonGroup } from '@oge-ui/buttons';
import type { OgeButtonGroupItem } from '@oge-ui/buttons';
@Component({
selector: 'demo-root',
imports: [OgeButtonGroup],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<oge-button-group
selectionMode="single"
[items]="periods"
[(selectedKeys)]="period"
size="sm"
ariaLabel="Period"
/>
`,
})
export class Demo {
protected readonly periods: OgeButtonGroupItem[] = [
{ value: 'day', text: 'Day' },
{ value: 'week', text: 'Week' },
{ value: 'month', text: 'Month' },
{ value: 'year', text: 'Year', disabled: true },
];
protected readonly period = signal<string[]>(['week']);
}Notes
- Roles adapt to the mode:
toolbar(none),radiogroup+role="radio"children (single),group+aria-pressedchildren (multiple). - Declarative children and the
itemsarray can be mixed; items render after the projected buttons. Icons need declarative children. selectedKeysalways receives a fresh array — safe for OnPush change detection andcomputed()consumers.- A group-level
disableddisables every child; individual buttons can still opt out of selection by omittingvalue.