Button
A clickable button component with multiple variants and states
Import
import { AvButtonComponent } from '@avesra/angular';Usage
import { Component } from '@angular/core';
import { AvButtonComponent } from '@avesra/angular';
@Component({
selector: 'app-button-basic-demo',
imports: [AvButtonComponent],
template: `<button av-button (click)="onPress()">Click me</button>`,
})
export class ButtonBasicDemo {
onPress(): void {
console.log('Button pressed');
}
}Variants
import { Component } from '@angular/core';
import { AvButtonComponent } from '@avesra/angular';
@Component({
selector: 'app-button-variants-demo',
imports: [AvButtonComponent],
template: `<div class="flex flex-wrap gap-3">
<button av-button>Primary</button>
<button av-button variant="secondary">Secondary</button>
<button av-button variant="tertiary">Tertiary</button>
<button av-button variant="outline">Outline</button>
<button av-button variant="ghost">Ghost</button>
<button av-button variant="danger">Danger</button>
<button av-button variant="danger-soft">Danger Soft</button>
</div>`,
})
export class ButtonVariantsDemo {}Sizes
import { Component } from '@angular/core';
import { AvButtonComponent } from '@avesra/angular';
@Component({
selector: 'app-button-sizes-demo',
imports: [AvButtonComponent],
template: `<div class="flex flex-wrap items-center gap-3">
<button av-button size="sm">Small</button>
<button av-button size="md">Medium</button>
<button av-button size="lg">Large</button>
</div>`,
})
export class ButtonSizesDemo {}With Icons
import { Component } from '@angular/core';
import { AvButtonComponent } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-button-with-icons-demo',
imports: [AvButtonComponent, AppIconComponent],
template: `<div class="flex flex-wrap gap-3">
<button av-button>
<app-icon icon="solar:global-linear" size="16" />
Search
</button>
<button av-button variant="secondary">
<app-icon icon="solar:add-circle-linear" size="16" />
Add Member
</button>
<button av-button variant="tertiary">
<app-icon icon="solar:letter-linear" size="16" />
Email
</button>
<button av-button variant="danger">
<app-icon icon="solar:trash-bin-trash-linear" size="16" />
Delete
</button>
</div>`,
})
export class ButtonWithIconsDemo {}Icon Only
import { Component } from '@angular/core';
import { AvButtonComponent } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-button-icon-only-demo',
imports: [AvButtonComponent, AppIconComponent],
template: `<div class="flex gap-3">
<button av-button variant="tertiary" icon-only aria-label="More options">
<app-icon icon="solar:menu-dots-linear" size="16" />
</button>
<button av-button variant="secondary" icon-only aria-label="Settings">
<app-icon icon="solar:settings-linear" size="16" />
</button>
<button av-button variant="danger" icon-only aria-label="Delete">
<app-icon icon="solar:trash-bin-trash-linear" size="16" />
</button>
</div>`,
})
export class ButtonIconOnlyDemo {}Loading
import { Component } from '@angular/core';
import { AvButtonComponent } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-button-loading-demo',
imports: [AvButtonComponent, AppIconComponent],
template: `<button av-button [pending]="true">
<app-icon icon="solar:refresh-linear" size="16" class="animate-spin" />
Uploading...
</button>`,
})
export class ButtonLoadingDemo {}Loading State
import { Component, signal } from '@angular/core';
import { AvButtonComponent } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-button-loading-state-demo',
imports: [AvButtonComponent, AppIconComponent],
template: `<button av-button [pending]="isLoading()" (click)="handlePress()">
@if (isLoading()) {
<app-icon icon="solar:refresh-linear" size="16" class="animate-spin" />
Uploading...
} @else {
<app-icon icon="solar:paperclip-linear" size="16" />
Upload File
}
</button>`,
})
export class ButtonLoadingStateDemo {
readonly isLoading = signal(false);
handlePress(): void {
this.isLoading.set(true);
setTimeout(() => this.isLoading.set(false), 2000);
}
}Full Width
import { Component } from '@angular/core';
import { AvButtonComponent } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-button-full-width-demo',
imports: [AvButtonComponent, AppIconComponent],
template: `<div class="w-[400px] space-y-3">
<button av-button full-width>Primary Button</button>
<button av-button full-width>
<app-icon icon="solar:add-circle-linear" size="16" />
With Icon
</button>
</div>`,
})
export class ButtonFullWidthDemo {}Disabled State
import { Component } from '@angular/core';
import { AvButtonComponent } from '@avesra/angular';
@Component({
selector: 'app-button-disabled-demo',
imports: [AvButtonComponent],
template: `<div class="flex flex-wrap gap-3">
<button av-button [disabled]="true">Primary</button>
<button av-button variant="secondary" [disabled]="true">Secondary</button>
<button av-button variant="tertiary" [disabled]="true">Tertiary</button>
<button av-button variant="outline" [disabled]="true">Outline</button>
<button av-button variant="ghost" [disabled]="true">Ghost</button>
<button av-button variant="danger" [disabled]="true">Danger</button>
<button av-button variant="danger-soft" [disabled]="true">Danger Soft</button>
</div>`,
})
export class ButtonDisabledDemo {}Render Function
In Angular, customize the host element directly with attributes — no render prop needed. Here data-custom tracks the pressed state.
import { Component } from '@angular/core';
import { AvButtonComponent } from '@avesra/angular';
@Component({
selector: 'app-button-render-function-demo',
imports: [AvButtonComponent],
template: `<button
av-button
[attr.data-custom]="pressed ? 'pressed' : 'bar'"
(pointerdown)="pressed = true"
(pointerup)="pressed = false"
(pointerleave)="pressed = false"
>
Press me
</button>`,
})
export class ButtonRenderFunctionDemo {
pressed = false;
}Adding custom variants
You can extend Avesra components by wrapping them and adding your own custom variants.
import { Component } from '@angular/core';
import { AvButtonComponent } from '@avesra/angular';
@Component({
selector: 'app-button-custom-variants-demo',
imports: [AvButtonComponent],
template: `<button
av-button
class="h-11 rounded-full px-6 text-md font-semibold shadow-md"
>
Custom Button
</button>`,
})
export class ButtonCustomVariantsDemo {}Adding Ripple Effect
The Button component supports ripple effects through composition, allowing you to nest ripple elements as children.
import { Component } from '@angular/core';
import { AvButtonComponent } from '@avesra/angular';
@Component({
selector: 'app-button-ripple-effect-demo',
imports: [AvButtonComponent],
template: `<button av-button variant="secondary" class="relative overflow-hidden">
<span class="ripple-dot" aria-hidden="true"></span>
Click me
</button>`,
styles: [`
.ripple-dot {
position: absolute;
inset: 50% auto auto 50%;
width: 0;
height: 0;
border-radius: 9999px;
background: currentColor;
opacity: 0.2;
transform: translate(-50%, -50%);
animation: ripple-pulse 1.5s ease-out infinite;
pointer-events: none;
}
@keyframes ripple-pulse {
0% { width: 0; height: 0; opacity: 0.3; }
100% { width: 200%; height: 200%; opacity: 0; }
}
`],
})
export class ButtonRippleEffectDemo {}Styling
Passing Tailwind CSS classes
Pass utility classes on the button host to customize shape, color, and decoration.
import { Component } from '@angular/core';
import { AvButtonComponent } from '@avesra/angular';
@Component({
selector: 'app-button-custom-styles-demo',
imports: [AvButtonComponent],
template: `<div class="relative inline-flex items-center justify-center">
<span
class="pointer-events-none absolute inset-[-28%] rounded-full bg-accent/25 blur-2xl dark:bg-accent/35"
aria-hidden="true"
></span>
<button
av-button
variant="ghost"
class="relative rounded-full border-0 bg-foreground px-9 py-3 text-[15px] font-medium tracking-wide text-background shadow-[inset_0_1px_0_rgba(255,255,255,0.18),0_10px_28px_-12px_rgba(0,0,0,0.45)] transition-[transform,box-shadow,filter] duration-300 ease-out hover:-translate-y-0.5 hover:shadow-[inset_0_1px_0_rgba(255,255,255,0.22),0_16px_36px_-12px_rgba(0,0,0,0.5)] hover:brightness-110 active:translate-y-0 active:scale-[0.98] dark:bg-white dark:text-neutral-950 dark:shadow-[inset_0_1px_0_rgba(255,255,255,0.9),0_12px_32px_-10px_rgba(255,255,255,0.35)]"
>
Upgrade
</button>
</div>`,
})
export class ButtonCustomStylesDemo {}Customizing the component classes
To customize the Button classes, use the @layer components directive. Learn more.
@layer components {
.av-button {
@apply bg-purple-500 text-white hover:bg-purple-600;
}
.av-button--icon-only {
@apply rounded-lg bg-blue-500;
}
}Avesra follows the BEM methodology so component variants and states stay reusable and easy to customize.
CSS Classes
The Button component uses these CSS classes:
Base & Size Classes
.av-button— Base button styles.av-button--sm— Small size variant.av-button--md— Medium size variant.av-button--lg— Large size variant
Variant Classes
.av-button--primary.av-button--secondary.av-button--tertiary.av-button--outline.av-button--ghost.av-button--danger.av-button--danger-soft
Modifier Classes
.av-button--icon-only.av-button--icon-only.av-button--sm.av-button--icon-only.av-button--lg.av-button--full-width
Interactive States
The button supports both CSS pseudo-classes and data attributes for flexibility:
- Hover:
:hoveror[data-hovered="true"] - Active/Pressed:
:activeor[data-pressed="true"](includes scale transform) - Focus:
:focus-visibleor[data-focus-visible="true"](shows focus ring) - Disabled:
:disabledor[aria-disabled="true"](reduced opacity, no pointer events) - Pending:
[data-pending](no pointer events during loading)
API Reference
Apply av-button on a native <button>. Use DOM events such as (click) for interaction. When nested in Button Group, omitted variant/size/disabled inherit from the group.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'primary' | 'secondary' | 'tertiary' | 'outline' | 'ghost' | 'danger' | 'danger-soft' | 'primary' | Visual style variant. Inherits from `av-button-group` when omitted. |
size | 'sm' | 'md' | 'lg' | 'md' | Size of the button. Inherits from `av-button-group` when omitted. |
fullWidth | boolean | false | Whether the button should take full width of its container. Template alias: full-width. |
disabled | boolean | undefined | undefined | Whether the button is disabled. When omitted, `pending` disables the button; otherwise inherits the parent `av-button-group` disabled state. |
pending | boolean | false | Whether the button is in a loading state (`data-pending`). |
iconOnly | boolean | false | Whether the button contains only an icon. Template alias: icon-only. Pair with `aria-label`. |
type | 'button' | 'submit' | 'reset' | 'button' | Native button type. |
Social Buttons