ButtonGroup
Group related buttons together with consistent styling and spacing
Import
import { AvButtonGroupImports } from '@avesra/angular';Usage
import { Component } from '@angular/core';
import {
AvButtonGroupImports,
AvChipImports,
AvDescriptionComponent,
AvDropdownImports,
AvLabelComponent,
} from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-button-group-basic-demo',
imports: [
AvButtonGroupImports,
AvChipImports,
AvDropdownImports,
AvLabelComponent,
AvDescriptionComponent,
AppIconComponent,
],
template: `<div class="flex flex-col items-start gap-6">
<div class="flex flex-col gap-2">
<av-dropdown>
<av-button-group>
<button av-button>Merge pull request</button>
<button av-button icon-only aria-label="More options" av-dropdown-trigger>
<span av-button-group-separator></span>
<app-icon icon="solar:alt-arrow-down-linear" size="16" />
</button>
</av-button-group>
<av-dropdown-popover class="max-w-[290px]" placement="bottom end">
<div av-dropdown-menu>
<div
av-menu-item
id="merge"
textValue="Create a merge commit"
class="flex flex-col items-start gap-1"
>
<label av-label>Create a merge commit</label>
<p av-description>
All commits from this branch will be added to the base branch
</p>
</div>
<div
av-menu-item
id="squash-and-merge"
textValue="Squash and merge"
class="flex flex-col items-start gap-1"
>
<label av-label>Squash and merge</label>
<p av-description>
The 14 commits from this branch will be combined into one commit in the base
branch
</p>
</div>
<div
av-menu-item
id="rebase-and-merge"
textValue="Rebase and merge"
class="flex flex-col items-start gap-1"
>
<label av-label>Rebase and merge</label>
<p av-description>
The 14 commits from this branch will be rebased and added to the base branch
</p>
</div>
</div>
</av-dropdown-popover>
</av-dropdown>
</div>
<div class="flex flex-col gap-2">
<div class="flex flex-wrap gap-x-2 gap-y-4">
<av-button-group variant="tertiary">
<button av-button>
<app-icon icon="solar:branching-paths-linear" size="14" />
Fork
<span av-chip color="accent" size="sm" variant="soft">24</span>
</button>
<button av-button icon-only aria-label="Fork options">
<span av-button-group-separator></span>
<app-icon icon="solar:alt-arrow-down-linear" size="16" />
</button>
</av-button-group>
<av-button-group variant="tertiary">
<button av-button icon-only aria-label="Scan QR code">
<app-icon icon="solar:qr-code-linear" size="16" />
</button>
<button av-button>
<span av-button-group-separator></span>
Scan to pay
</button>
</av-button-group>
<av-button-group variant="tertiary">
<button av-button>
<app-icon icon="solar:like-linear" size="16" />
<span class="text-xs font-semibold">2.4K</span>
</button>
<button av-button icon-only aria-label="Dislike">
<span av-button-group-separator></span>
<app-icon icon="solar:dislike-linear" size="16" />
</button>
</av-button-group>
<av-button-group variant="tertiary">
<button av-button>
<app-icon icon="solar:star-linear" size="14" />
Star
</button>
<button av-button class="px-2">
<span av-button-group-separator></span>
<span av-chip color="accent" size="sm" variant="soft">104</span>
</button>
</av-button-group>
<av-button-group variant="tertiary">
<button av-button>
<app-icon icon="solar:pin-linear" size="16" />
Pinned
</button>
<button av-button icon-only aria-label="Pin options">
<span av-button-group-separator></span>
<app-icon icon="solar:alt-arrow-down-linear" size="16" />
</button>
</av-button-group>
</div>
</div>
<div class="flex flex-col gap-2">
<av-button-group variant="tertiary">
<button av-button>
<app-icon icon="solar:alt-arrow-left-linear" size="16" />
Previous
</button>
<button av-button>
<span av-button-group-separator></span>
Next
<app-icon icon="solar:alt-arrow-right-linear" size="16" />
</button>
</av-button-group>
</div>
<div class="flex flex-col gap-2">
<av-button-group variant="tertiary">
<button av-button>
<app-icon icon="solar:gallery-linear" size="16" />
Photos
</button>
<button av-button>
<span av-button-group-separator></span>
<app-icon icon="solar:videocamera-linear" size="16" />
Videos
</button>
<button av-button icon-only aria-label="More options">
<span av-button-group-separator></span>
<app-icon icon="solar:menu-dots-linear" size="16" />
</button>
</av-button-group>
</div>
<div class="flex flex-col gap-2">
<av-button-group variant="tertiary">
<button av-button>Left</button>
<button av-button>
<span av-button-group-separator></span>
Center
</button>
<button av-button>
<span av-button-group-separator></span>
Right
</button>
</av-button-group>
</div>
<div class="flex flex-col gap-2">
<av-button-group variant="tertiary">
<button av-button icon-only aria-label="Align left">
<app-icon icon="solar:align-left-linear" size="16" />
</button>
<button av-button icon-only aria-label="Align top">
<span av-button-group-separator></span>
<app-icon icon="solar:align-top-linear" size="16" />
</button>
<button av-button icon-only aria-label="Align right">
<span av-button-group-separator></span>
<app-icon icon="solar:align-right-linear" size="16" />
</button>
<button av-button icon-only aria-label="Align bottom">
<span av-button-group-separator></span>
<app-icon icon="solar:align-bottom-linear" size="16" />
</button>
</av-button-group>
</div>
</div>`,
})
export class ButtonGroupBasicDemo {}Anatomy
<av-button-group>
<button av-button>First</button>
<button av-button>
<span av-button-group-separator></span>
Second
</button>
<button av-button>
<span av-button-group-separator></span>
Third
</button>
</av-button-group>ButtonGroup wraps multiple Button components together, applying consistent styling, spacing, and automatic border radius handling. It uses context to pass size, variant, and disabled to all child buttons.
Variants
Primary
Secondary
Tertiary
Outline
Ghost
Danger
import { Component } from '@angular/core';
import { AvButtonGroupImports } from '@avesra/angular';
@Component({
selector: 'app-button-group-variants-demo',
imports: [AvButtonGroupImports],
template: `<div class="flex flex-col gap-6">
<div class="flex flex-col gap-2">
<p class="text-sm text-muted">Primary</p>
<av-button-group variant="primary">
<button av-button>First</button>
<button av-button><span av-button-group-separator></span>Second</button>
<button av-button><span av-button-group-separator></span>Third</button>
</av-button-group>
</div>
<div class="flex flex-col gap-2">
<p class="text-sm text-muted">Secondary</p>
<av-button-group variant="secondary">
<button av-button>First</button>
<button av-button><span av-button-group-separator></span>Second</button>
<button av-button><span av-button-group-separator></span>Third</button>
</av-button-group>
</div>
<div class="flex flex-col gap-2">
<p class="text-sm text-muted">Tertiary</p>
<av-button-group variant="tertiary">
<button av-button>First</button>
<button av-button><span av-button-group-separator></span>Second</button>
<button av-button><span av-button-group-separator></span>Third</button>
</av-button-group>
</div>
<div class="flex flex-col gap-2">
<p class="text-sm text-muted">Outline</p>
<av-button-group variant="outline">
<button av-button>First</button>
<button av-button><span av-button-group-separator></span>Second</button>
<button av-button><span av-button-group-separator></span>Third</button>
</av-button-group>
</div>
<div class="flex flex-col gap-2">
<p class="text-sm text-muted">Ghost</p>
<av-button-group variant="ghost">
<button av-button>First</button>
<button av-button><span av-button-group-separator></span>Second</button>
<button av-button><span av-button-group-separator></span>Third</button>
</av-button-group>
</div>
<div class="flex flex-col gap-2">
<p class="text-sm text-muted">Danger</p>
<av-button-group variant="danger">
<button av-button>First</button>
<button av-button><span av-button-group-separator></span>Second</button>
<button av-button><span av-button-group-separator></span>Third</button>
</av-button-group>
</div>
</div>`,
})
export class ButtonGroupVariantsDemo {}Sizes
Small
Medium (default)
Large
import { Component } from '@angular/core';
import { AvButtonGroupImports } from '@avesra/angular';
@Component({
selector: 'app-button-group-sizes-demo',
imports: [AvButtonGroupImports],
template: `<div class="flex flex-col gap-4">
<div class="flex flex-col items-start gap-2">
<p class="text-sm text-muted">Small</p>
<av-button-group size="sm" variant="secondary">
<button av-button>First</button>
<button av-button><span av-button-group-separator></span>Second</button>
<button av-button><span av-button-group-separator></span>Third</button>
</av-button-group>
</div>
<div class="flex flex-col items-start gap-2">
<p class="text-sm text-muted">Medium (default)</p>
<av-button-group size="md" variant="secondary">
<button av-button>First</button>
<button av-button><span av-button-group-separator></span>Second</button>
<button av-button><span av-button-group-separator></span>Third</button>
</av-button-group>
</div>
<div class="flex flex-col items-start gap-2">
<p class="text-sm text-muted">Large</p>
<av-button-group size="lg" variant="secondary">
<button av-button>First</button>
<button av-button><span av-button-group-separator></span>Second</button>
<button av-button><span av-button-group-separator></span>Third</button>
</av-button-group>
</div>
</div>`,
})
export class ButtonGroupSizesDemo {}Orientation
Use the orientation prop to arrange buttons horizontally or vertically.
import { Component } from '@angular/core';
import { AvButtonGroupImports } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-button-group-orientation-demo',
imports: [AvButtonGroupImports, 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-button-group orientation="horizontal" variant="tertiary">
<button av-button icon-only aria-label="Align left">
<app-icon icon="solar:align-left-linear" size="16" />
</button>
<button av-button icon-only aria-label="Align top">
<span av-button-group-separator></span>
<app-icon icon="solar:align-top-linear" size="16" />
</button>
<button av-button icon-only aria-label="Align right">
<span av-button-group-separator></span>
<app-icon icon="solar:align-right-linear" size="16" />
</button>
<button av-button icon-only aria-label="Align bottom">
<span av-button-group-separator></span>
<app-icon icon="solar:align-bottom-linear" size="16" />
</button>
</av-button-group>
</div>
<div class="flex flex-col gap-2">
<span class="text-sm text-muted">Vertical</span>
<av-button-group orientation="vertical" variant="tertiary">
<button av-button icon-only aria-label="Align left">
<app-icon icon="solar:align-left-linear" size="16" />
</button>
<button av-button icon-only aria-label="Align top">
<span av-button-group-separator></span>
<app-icon icon="solar:align-top-linear" size="16" />
</button>
<button av-button icon-only aria-label="Align right">
<span av-button-group-separator></span>
<app-icon icon="solar:align-right-linear" size="16" />
</button>
<button av-button icon-only aria-label="Align bottom">
<span av-button-group-separator></span>
<app-icon icon="solar:align-bottom-linear" size="16" />
</button>
</av-button-group>
</div>
</div>`,
})
export class ButtonGroupOrientationDemo {}With Icons
With icons
Icon only buttons
import { Component } from '@angular/core';
import { AvButtonGroupImports } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-button-group-with-icons-demo',
imports: [AvButtonGroupImports, AppIconComponent],
template: `<div class="flex flex-col gap-6">
<div class="flex flex-col items-start gap-2">
<p class="text-sm text-muted">With icons</p>
<av-button-group variant="secondary">
<button av-button>
<app-icon icon="solar:global-linear" size="16" />
Search
</button>
<button av-button>
<span av-button-group-separator></span>
<app-icon icon="solar:add-square-linear" size="16" />
Add
</button>
<button av-button>
<span av-button-group-separator></span>
<app-icon icon="solar:trash-bin-trash-linear" size="16" />
Delete
</button>
</av-button-group>
</div>
<div class="flex flex-col items-start gap-2">
<p class="text-sm text-muted">Icon only buttons</p>
<av-button-group variant="tertiary">
<button av-button icon-only aria-label="Search">
<app-icon icon="solar:global-linear" size="16" />
</button>
<button av-button icon-only aria-label="Add">
<span av-button-group-separator></span>
<app-icon icon="solar:add-square-linear" size="16" />
</button>
<button av-button icon-only aria-label="Delete">
<span av-button-group-separator></span>
<app-icon icon="solar:trash-bin-trash-linear" size="16" />
</button>
</av-button-group>
</div>
</div>`,
})
export class ButtonGroupWithIconsDemo {}Full Width
import { Component } from '@angular/core';
import { AvButtonGroupImports } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-button-group-full-width-demo',
imports: [AvButtonGroupImports, AppIconComponent],
template: `<div class="w-[400px] space-y-3">
<av-button-group full-width>
<button av-button>First</button>
<button av-button><span av-button-group-separator></span>Second</button>
<button av-button><span av-button-group-separator></span>Third</button>
</av-button-group>
<av-button-group full-width>
<button av-button icon-only aria-label="Align left">
<app-icon icon="solar:align-left-linear" size="16" />
</button>
<button av-button icon-only aria-label="Align top">
<span av-button-group-separator></span>
<app-icon icon="solar:align-top-linear" size="16" />
</button>
<button av-button icon-only aria-label="Align right">
<span av-button-group-separator></span>
<app-icon icon="solar:align-right-linear" size="16" />
</button>
<button av-button icon-only aria-label="Align bottom">
<span av-button-group-separator></span>
<app-icon icon="solar:align-bottom-linear" size="16" />
</button>
</av-button-group>
</div>`,
})
export class ButtonGroupFullWidthDemo {}Disabled State
All buttons disabled
Group disabled, but one button overrides
import { Component } from '@angular/core';
import { AvButtonGroupImports } from '@avesra/angular';
@Component({
selector: 'app-button-group-disabled-demo',
imports: [AvButtonGroupImports],
template: `<div class="flex flex-col gap-6">
<div class="flex flex-col items-start gap-2">
<p class="text-sm text-muted">All buttons disabled</p>
<av-button-group [disabled]="true">
<button av-button>First</button>
<button av-button><span av-button-group-separator></span>Second</button>
<button av-button><span av-button-group-separator></span>Third</button>
</av-button-group>
</div>
<div class="flex flex-col items-start gap-2">
<p class="text-sm text-muted">Group disabled, but one button overrides</p>
<av-button-group [disabled]="true">
<button av-button>First</button>
<button av-button><span av-button-group-separator></span>Second</button>
<button av-button [disabled]="false"><span av-button-group-separator></span>Third (enabled)</button>
</av-button-group>
</div>
</div>`,
})
export class ButtonGroupDisabledDemo {}Without Separator
Simply omit the av-button-group-separator element from your buttons.
import { Component } from '@angular/core';
import { AvButtonGroupImports } from '@avesra/angular';
@Component({
selector: 'app-button-group-without-separator-demo',
imports: [AvButtonGroupImports],
template: `<av-button-group>
<button av-button>First</button>
<button av-button>Second</button>
<button av-button>Third</button>
</av-button-group>`,
})
export class ButtonGroupWithoutSeparatorDemo {}Styling
Passing Tailwind CSS classes
Pass utility classes on the group and child buttons to customize color, radius, and spacing.
import { Component } from '@angular/core';
import { AvButtonGroupImports } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-button-group-custom-styles-demo',
imports: [
AvButtonGroupImports,
AppIconComponent,
],
template: `<av-button-group>
<button av-button class="rounded-none bg-[#08872B] hover:bg-[#0FBF3E]">
Merge pull request
</button>
<button
av-button
icon-only
aria-label="Merge options"
class="rounded-none bg-[#08872B] hover:bg-[#0FBF3E]"
>
<span av-button-group-separator></span>
<app-icon icon="solar:alt-arrow-down-linear" size="16" />
</button>
</av-button-group>`,
})
export class ButtonGroupCustomStylesDemo {}Customizing the component classes
To customize the ButtonGroup classes, use the @layer components directive. Learn more.
@layer components {
.av-button-group {
@apply gap-2 rounded-lg;
}
.av-button-group__separator {
@apply opacity-25;
}
}Avesra follows the BEM methodology so component variants and states stay reusable and easy to customize.
CSS Classes
The ButtonGroup component uses these CSS classes:
Base Classes
.av-button-group— Base button group container.av-button-group--full-width— Full width modifier.av-button-group--horizontal— Horizontal layout.av-button-group--vertical— Vertical layout.av-button-group__separator— Separator element between buttons
The ButtonGroup component automatically applies border radius to buttons:
- First button gets rounded left/start edge
- Last button gets rounded right/end edge
- Middle buttons have no border radius
- Single button gets full border radius on all edges
Add av-button-group-separator inside each Button (except the first) to show dividers between buttons.
Note
- ButtonGroup uses context to pass
size,variant, anddisabledto all child Button components - Only direct child buttons receive the ButtonGroup props — Buttons nested inside other components (like Modal, Dropdown, etc.) will not inherit the group's props even if they are descendants of the ButtonGroup
- Individual Button components can override the group's
disabledprop by setting[disabled]="false" - The component automatically handles border radius between buttons
- Add
av-button-group-separatorinside each Button (except the first) to show dividers between buttons - Buttons in a group have their active/pressed scale transform removed for a more cohesive appearance
- When the group represents a single choice, consider Toggle Button Group instead
API Reference
Compound API across av-button-group, child button[av-button], and optional av-button-group-separator.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'primary' | 'secondary' | 'tertiary' | 'outline' | 'ghost' | 'danger' | 'danger-soft' | — | Visual style variant applied to all buttons in the group. |
size | 'sm' | 'md' | 'lg' | — | Size applied to all buttons in the group. |
orientation | 'horizontal' | 'vertical' | 'horizontal' | The orientation of the button group. |
fullWidth | boolean | false | Whether the button group should take full width of its container. Template alias: full-width. |
disabled | boolean | false | Whether all buttons in the group are disabled (can be overridden on individual buttons). |