ToggleButtonGroup
Groups multiple ToggleButtons into a unified control, allowing users to select one or multiple options.
Import
import { AvToggleButtonGroupImports } from '@avesra/angular';Usage
import { Component } from '@angular/core';
import { AvToggleButtonGroupImports } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-toggle-button-group-basic-demo',
imports: [AvToggleButtonGroupImports, AppIconComponent],
template: `<av-toggle-button-group selection-mode="multiple">
<button av-toggle-button icon-only value="bold" aria-label="Bold">
<app-icon icon="solar:text-bold-linear" size="16" />
</button>
<button av-toggle-button icon-only value="italic" aria-label="Italic">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-italic-linear" size="16" />
</button>
<button av-toggle-button icon-only value="underline" aria-label="Underline">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-underline-linear" size="16" />
</button>
<button av-toggle-button icon-only value="strikethrough" aria-label="Strikethrough">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-cross-linear" size="16" />
</button>
</av-toggle-button-group>`,
})
export class ToggleButtonGroupBasicDemo {}Anatomy
Wrap button[av-toggle-button] elements in av-toggle-button-group. Each button needs a unique value. Add span[av-toggle-button-group-separator] inside each button except the first to show dividers when attached.
<av-toggle-button-group selection-mode="multiple">
<button av-toggle-button value="first">First</button>
<button av-toggle-button value="second">
<span av-toggle-button-group-separator></span>
Second
</button>
<button av-toggle-button value="third">
<span av-toggle-button-group-separator></span>
Third
</button>
</av-toggle-button-group>Sizes
Set size on the group to propagate a shared size to all child toggle buttons.
// See component preview for full templateOrientation
Use orientation="horizontal" (default) or orientation="vertical" to change the layout direction.
import { Component } from '@angular/core';
import { AvToggleButtonGroupImports } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-toggle-button-group-orientation-demo',
imports: [AvToggleButtonGroupImports, AppIconComponent],
template: `<div class="flex items-start gap-8">
<div class="flex flex-col gap-2">
<span class="text-sm text-muted">Horizontal</span>
<av-toggle-button-group orientation="horizontal" selection-mode="multiple">
<button av-toggle-button icon-only value="bold" aria-label="Bold">
<app-icon icon="solar:text-bold-linear" size="16" />
</button>
<button av-toggle-button icon-only value="italic" aria-label="Italic">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-italic-linear" size="16" />
</button>
<button av-toggle-button icon-only value="underline" aria-label="Underline">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-underline-linear" size="16" />
</button>
</av-toggle-button-group>
</div>
<div class="flex flex-col gap-2">
<span class="text-sm text-muted">Vertical</span>
<av-toggle-button-group orientation="vertical" selection-mode="multiple">
<button av-toggle-button icon-only value="bold" aria-label="Bold">
<app-icon icon="solar:text-bold-linear" size="16" />
</button>
<button av-toggle-button icon-only value="italic" aria-label="Italic">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-italic-linear" size="16" />
</button>
<button av-toggle-button icon-only value="underline" aria-label="Underline">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-underline-linear" size="16" />
</button>
</av-toggle-button-group>
</div>
</div>`,
})
export class ToggleButtonGroupOrientationDemo {}Full Width
Use full-width to expand the group and distribute buttons evenly.
import { Component } from '@angular/core';
import { AvToggleButtonGroupImports } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-toggle-button-group-full-width-demo',
imports: [AvToggleButtonGroupImports, AppIconComponent],
template: `<div class="w-full max-w-md space-y-3">
<av-toggle-button-group full-width selection-mode="multiple">
<button av-toggle-button icon-only value="bold" aria-label="Bold">
<app-icon icon="solar:text-bold-linear" size="16" />
</button>
<button av-toggle-button icon-only value="italic" aria-label="Italic">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-italic-linear" size="16" />
</button>
<button av-toggle-button icon-only value="underline" aria-label="Underline">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-underline-linear" size="16" />
</button>
<button av-toggle-button icon-only value="strikethrough" aria-label="Strikethrough">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-cross-linear" size="16" />
</button>
</av-toggle-button-group>
<av-toggle-button-group full-width selection-mode="single">
<button av-toggle-button value="left">
<app-icon icon="solar:align-left-linear" size="16" />
Left
</button>
<button av-toggle-button value="top">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:align-top-linear" size="16" />
Top
</button>
<button av-toggle-button value="right">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:align-right-linear" size="16" />
Right
</button>
<button av-toggle-button value="bottom">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:align-bottom-linear" size="16" />
Bottom
</button>
</av-toggle-button-group>
</div>`,
})
export class ToggleButtonGroupFullWidthDemo {}Disabled
Set disabled on the group to disable all child toggle buttons.
import { Component } from '@angular/core';
import { AvToggleButtonGroupImports } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-toggle-button-group-disabled-demo',
imports: [AvToggleButtonGroupImports, AppIconComponent],
template: `<div class="flex flex-col gap-6">
<div class="flex flex-col gap-2">
<span class="text-sm text-muted">All buttons disabled</span>
<av-toggle-button-group selection-mode="multiple" [disabled]="true">
<button av-toggle-button icon-only value="bold" aria-label="Bold">
<app-icon icon="solar:text-bold-linear" size="16" />
</button>
<button av-toggle-button icon-only value="italic" aria-label="Italic">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-italic-linear" size="16" />
</button>
<button av-toggle-button icon-only value="underline" aria-label="Underline">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-underline-linear" size="16" />
</button>
</av-toggle-button-group>
</div>
<div class="flex flex-col gap-2">
<span class="text-sm text-muted">Individual button disabled</span>
<av-toggle-button-group selection-mode="multiple">
<button av-toggle-button icon-only value="bold" aria-label="Bold">
<app-icon icon="solar:text-bold-linear" size="16" />
</button>
<button av-toggle-button icon-only value="italic" aria-label="Italic" [disabled]="true">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-italic-linear" size="16" />
</button>
<button av-toggle-button icon-only value="underline" aria-label="Underline">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-underline-linear" size="16" />
</button>
</av-toggle-button-group>
</div>
</div>`,
})
export class ToggleButtonGroupDisabledDemo {}Without Separator
Simply omit span[av-toggle-button-group-separator] from your buttons.
import { Component } from '@angular/core';
import { AvToggleButtonGroupImports } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-toggle-button-group-without-separator-demo',
imports: [AvToggleButtonGroupImports, AppIconComponent],
template: `<av-toggle-button-group selection-mode="multiple">
<button av-toggle-button icon-only value="bold" aria-label="Bold">
<app-icon icon="solar:text-bold-linear" size="16" />
</button>
<button av-toggle-button icon-only value="italic" aria-label="Italic">
<app-icon icon="solar:text-italic-linear" size="16" />
</button>
<button av-toggle-button icon-only value="underline" aria-label="Underline">
<app-icon icon="solar:text-underline-linear" size="16" />
</button>
<button av-toggle-button icon-only value="strikethrough" aria-label="Strikethrough">
<app-icon icon="solar:text-cross-linear" size="16" />
</button>
</av-toggle-button-group>`,
})
export class ToggleButtonGroupWithoutSeparatorDemo {}Detached
Use [detached]="true" to separate buttons with gaps instead of connecting them.
import { Component } from '@angular/core';
import { AvToggleButtonGroupImports } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-toggle-button-group-attached-demo',
imports: [AvToggleButtonGroupImports, AppIconComponent],
template: `<div class="flex flex-col gap-6">
<div class="flex flex-col gap-2">
<span class="text-sm text-muted">Attached (default)</span>
<av-toggle-button-group selection-mode="multiple">
<button av-toggle-button icon-only value="bold" aria-label="Bold">
<app-icon icon="solar:text-bold-linear" size="16" />
</button>
<button av-toggle-button icon-only value="italic" aria-label="Italic">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-italic-linear" size="16" />
</button>
<button av-toggle-button icon-only value="underline" aria-label="Underline">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-underline-linear" size="16" />
</button>
<button av-toggle-button icon-only value="strikethrough" aria-label="Strikethrough">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-cross-linear" size="16" />
</button>
</av-toggle-button-group>
</div>
<div class="flex flex-col gap-2">
<span class="text-sm text-muted">Detached</span>
<av-toggle-button-group selection-mode="multiple" [detached]="true">
<button av-toggle-button icon-only value="bold" aria-label="Bold">
<app-icon icon="solar:text-bold-linear" size="16" />
</button>
<button av-toggle-button icon-only value="italic" aria-label="Italic">
<app-icon icon="solar:text-italic-linear" size="16" />
</button>
<button av-toggle-button icon-only value="underline" aria-label="Underline">
<app-icon icon="solar:text-underline-linear" size="16" />
</button>
<button av-toggle-button icon-only value="strikethrough" aria-label="Strikethrough">
<app-icon icon="solar:text-cross-linear" size="16" />
</button>
</av-toggle-button-group>
</div>
</div>`,
})
export class ToggleButtonGroupAttachedDemo {}Selection Mode
Use selection-mode="single" for mutually exclusive choices or selection-mode="multiple" for independent toggles.
import { Component } from '@angular/core';
import { AvToggleButtonGroupImports } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-toggle-button-group-selection-mode-demo',
imports: [AvToggleButtonGroupImports, AppIconComponent],
template: `<div class="flex flex-col gap-6">
<div class="flex flex-col gap-2">
<span class="text-sm text-muted">Single selection</span>
<av-toggle-button-group selection-mode="single" [default-selected-keys]="['top']">
<button av-toggle-button value="left">
<app-icon icon="solar:align-left-linear" size="16" />
Left
</button>
<button av-toggle-button value="top">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:align-top-linear" size="16" />
Top
</button>
<button av-toggle-button value="right">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:align-right-linear" size="16" />
Right
</button>
<button av-toggle-button value="bottom">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:align-bottom-linear" size="16" />
Bottom
</button>
</av-toggle-button-group>
</div>
<div class="flex flex-col gap-2">
<span class="text-sm text-muted">Multiple selection</span>
<av-toggle-button-group selection-mode="multiple" [default-selected-keys]="['bold', 'underline']">
<button av-toggle-button icon-only value="bold" aria-label="Bold">
<app-icon icon="solar:text-bold-linear" size="16" />
</button>
<button av-toggle-button icon-only value="italic" aria-label="Italic">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-italic-linear" size="16" />
</button>
<button av-toggle-button icon-only value="underline" aria-label="Underline">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-underline-linear" size="16" />
</button>
<button av-toggle-button icon-only value="strikethrough" aria-label="Strikethrough">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-cross-linear" size="16" />
</button>
</av-toggle-button-group>
</div>
</div>`,
})
export class ToggleButtonGroupSelectionModeDemo {}Controlled
Bind [(selectedKeys)] for two-way control over the selected button values.
Selected: bold
import { Component, computed, signal } from '@angular/core';
import { AvToggleButtonGroupImports } from '@avesra/angular';
@Component({
selector: 'app-toggle-button-group-controlled-demo',
imports: [AvToggleButtonGroupImports],
template: `<div class="flex flex-col gap-4">
<av-toggle-button-group selection-mode="multiple" [(selectedKeys)]="selectedKeys">
<button av-toggle-button icon-only value="bold" aria-label="Bold">
<app-icon icon="solar:text-bold-linear" size="16" />
</button>
<button av-toggle-button icon-only value="italic" aria-label="Italic">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-italic-linear" size="16" />
</button>
<button av-toggle-button icon-only value="underline" aria-label="Underline">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-underline-linear" size="16" />
</button>
<button av-toggle-button icon-only value="strikethrough" aria-label="Strikethrough">
<span av-toggle-button-group-separator></span>
<app-icon icon="solar:text-cross-linear" size="16" />
</button>
</av-toggle-button-group>
<p class="text-sm text-muted">
Selected:
<span class="font-medium">{{ selectedLabel() }}</span>
</p>
</div>`,
})
export class ToggleButtonGroupControlledDemo {
readonly selectedKeys = signal(['bold']);
readonly selectedLabel = computed(() =>
this.selectedKeys().length > 0 ? this.selectedKeys().join(', ') : 'None',
);
}Disallow Empty Selection
Set disallow-empty-selection with selection-mode="single" to ensure one option is always selected.
Selected: center
// See component preview for full templateCustomization
Tailwind CSS
import { Component } from '@angular/core';
import { AvToggleButtonGroupImports } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-toggle-button-group-custom-styles-demo',
imports: [AvToggleButtonGroupImports, AppIconComponent],
template: `
<av-toggle-button-group
class="gap-1 rounded-xl border border-border/80 bg-surface p-1 shadow-sm"
selection-mode="multiple"
aria-label="Text formatting"
>
<button
av-toggle-button
icon-only
class="rounded-lg text-muted data-[selected=true]:bg-accent-soft data-[selected=true]:text-accent-soft-foreground"
value="bold"
aria-label="Bold"
>
<app-icon icon="solar:text-bold-linear" size="16" />
</button>
<button
av-toggle-button
icon-only
class="rounded-lg text-muted data-[selected=true]:bg-accent-soft data-[selected=true]:text-accent-soft-foreground"
value="italic"
aria-label="Italic"
>
<app-icon icon="solar:text-italic-linear" size="16" />
</button>
<button
av-toggle-button
icon-only
class="rounded-lg text-muted data-[selected=true]:bg-accent-soft data-[selected=true]:text-accent-soft-foreground"
value="underline"
aria-label="Underline"
>
<app-icon icon="solar:text-underline-linear" size="16" />
</button>
</av-toggle-button-group>
`,
})
export class ToggleButtonGroupCustomStylesDemo {}Global CSS
To customize ToggleButtonGroup classes, use the @layer components directive.
@layer components {
.av-toggle-button-group {
@apply rounded-lg;
}
.av-toggle-button-group__separator {
@apply opacity-25;
}
.av-toggle-button-group--full-width {
@apply w-full;
}
}Styling Reference
Avesra follows the BEM methodology so variants and states stay reusable and easy to customize.
CSS Classes
Base & Layout Classes
.av-toggle-button-group— Base container styles.av-toggle-button-group--horizontal— Horizontal orientation.av-toggle-button-group--vertical— Vertical orientation.av-toggle-button-group--full-width— Full width modifier.av-toggle-button-group__separator— Separator between attached buttons
Modifier Classes
.av-toggle-button-group--detached— Detached mode (separated buttons with gaps)
API Reference
ToggleButtonGroup
| Prop | Type | Default | Description |
|---|---|---|---|
selection-mode | 'single' | 'multiple' | 'single' | Whether one or multiple buttons can be selected. |
selectedKeys | string[] | — | Controlled selection state. Supports two-way binding with [(selectedKeys)]. |
default-selected-keys | string[] | [] | Default selected keys (uncontrolled). |
disallow-empty-selection | boolean | false | Prevents clearing all selections. |
orientation | 'horizontal' | 'vertical' | 'horizontal' | Layout direction. |
size | 'sm' | 'md' | 'lg' | 'md' | Size propagated to child ToggleButtons. |
detached | boolean | false | Whether buttons are visually separated with gaps. |
full-width | boolean | false | Whether the group fills available width. |
disabled | boolean | false | Disables all buttons in the group. |
ToggleButtonGroup.Separator
| Prop | Type | Default | Description |
|---|---|---|---|
(host) | span[av-toggle-button-group-separator] | — | Decorative divider. Place inside each toggle button except the first. Hidden automatically in detached mode. |
Notes
- The group uses context to pass
sizeanddisabledto childav-toggle-buttoncomponents - Each toggle button must have a unique
valuethat corresponds to keys inselectedKeys/default-selected-keys - Group
disableddisables all children — an individual button can still set its owndisabled - Attached mode automatically handles border radius between buttons
- Add
span[av-toggle-button-group-separator]inside each button except the first to show dividers - Use
disallow-empty-selectionwithselection-mode="single"to ensure one option is always selected