Checkbox
Checkboxes allow users to select multiple items from a list of individual items, or to mark one individual item as selected.
Import
import { AvCheckboxImports } from '@avesra/angular';Usage
import { Component } from '@angular/core';
import { AvCheckboxImports } from '@avesra/angular';
@Component({
selector: 'app-checkbox-basic-demo',
imports: [AvCheckboxImports],
template: `<div av-checkbox name="basic-terms">
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<span av-checkbox-content>Accept terms and conditions</span>
</div>`,
})
export class CheckboxBasicDemo {}Anatomy
Import the Checkbox components and compose the parts with av-checkbox, av-checkbox-control, av-checkbox-indicator, and av-checkbox-content.
<div av-checkbox>
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<span av-checkbox-content>
Label <!-- plain text — the clickable label + accessible name -->
</span>
<p av-description></p> <!-- Optional — field-level help text -->
<p av-field-error></p> <!-- Optional — validation message -->
</div>Variants
Two visual styles — primary (default) and secondary.
import { Component } from '@angular/core';
import {
AvCheckboxImports,
AvLabelComponent,
} from '@avesra/angular';
@Component({
selector: 'app-checkbox-variants-demo',
imports: [AvCheckboxImports, AvLabelComponent],
host: { class: 'flex w-full items-center flex-col gap-3' },
template: `<div av-checkbox variant="primary">
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<span av-checkbox-content>
<label av-label class="text-sm">Primary checkbox</label>
</span>
</div>
<div av-checkbox variant="secondary">
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<span av-checkbox-content>
<label av-label class="text-sm">Secondary checkbox</label>
</span>
</div>`,
})
export class CheckboxVariantsDemo {}Disabled
This feature is coming soon
import { Component } from '@angular/core';
import {
AvCheckboxImports,
AvDescriptionComponent,
} from '@avesra/angular';
@Component({
selector: 'app-checkbox-disabled-demo',
imports: [
AvCheckboxImports,
AvDescriptionComponent,
],
template: `<div av-checkbox disabled id="feature">
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<span av-checkbox-content>
Premium Feature
<p av-description>This feature is coming soon</p>
</span>
</div>`,
})
export class CheckboxDisabledDemo {}External Label
Compose with av-label and matching for / id when the label lives outside the checkbox.
import { Component } from '@angular/core';
import {
AvCheckboxImports,
AvLabelComponent,
} from '@avesra/angular';
@Component({
selector: 'app-checkbox-external-label-demo',
imports: [
AvCheckboxImports,
AvLabelComponent,
],
template: `<div class="flex items-center gap-3">
<div av-checkbox id="label-marketing">
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<span av-checkbox-content></span>
</div>
<label av-label for="label-marketing">Send me marketing emails</label>
</div>`,
})
export class CheckboxExternalLabelDemo {}With Description
Add av-description inside av-checkbox-content for supplementary helper text.
I agree to the terms and privacy policy
import { Component } from '@angular/core';
import {
AvCheckboxImports,
AvDescriptionComponent,
AvLabelComponent,
} from '@avesra/angular';
@Component({
selector: 'app-checkbox-with-description-demo',
imports: [AvCheckboxImports, AvLabelComponent, AvDescriptionComponent],
host: { class: 'flex w-full items-center flex-col gap-3' },
template: `<div av-checkbox>
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<span av-checkbox-content>
<label av-label class="text-sm">Accept terms and conditions</label>
<p av-description>I agree to the terms and privacy policy</p>
</span>
</div>`,
})
export class CheckboxWithDescriptionDemo {}Default Selected
Use default-selected for an initially checked uncontrolled checkbox.
import { Component } from '@angular/core';
import { AvCheckboxImports } from '@avesra/angular';
@Component({
selector: 'app-checkbox-default-selected-demo',
imports: [AvCheckboxImports],
template: `<div av-checkbox default-selected id="default-notifications">
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<span av-checkbox-content>Enable email notifications</span>
</div>`,
})
export class CheckboxDefaultSelectedDemo {}Invalid
Set invalid and pair with av-field-error to communicate validation failures.
You must accept the terms to continue
import { Component } from '@angular/core';
import {
AvCheckboxImports,
AvFieldErrorComponent,
} from '@avesra/angular';
@Component({
selector: 'app-checkbox-invalid-demo',
imports: [
AvCheckboxImports,
AvFieldErrorComponent,
],
template: `<div av-checkbox invalid name="agreement">
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<span av-checkbox-content>I agree to the terms</span>
<p av-field-error [visible]="true">You must accept the terms to continue</p>
</div>`,
})
export class CheckboxInvalidDemo {}Controlled
Bind [(selected)] for two-way control over the selected state.
Checkbox is unchecked
import { Component, signal } from '@angular/core';
import {
AvCheckboxImports,
AvLabelComponent,
} from '@avesra/angular';
@Component({
selector: 'app-checkbox-controlled-demo',
imports: [AvCheckboxImports, AvLabelComponent],
host: { class: 'flex w-full items-center flex-col gap-3' },
template: `<div av-checkbox [(selected)]="terms">
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<label av-label class="text-sm">Accept terms</label>
</div>
<p class="text-sm text-muted">Checkbox is {{ terms() ? 'checked' : 'unchecked' }}</p>`,
})
export class CheckboxControlledDemo {
readonly terms = signal(false);
}Indeterminate
Bind indeterminate for tri-state checkboxes, commonly used for “select all” patterns.
Shows indeterminate state (dash icon)
import { Component, signal } from '@angular/core';
import {
AvCheckboxImports,
AvDescriptionComponent,
} from '@avesra/angular';
@Component({
selector: 'app-checkbox-indeterminate-demo',
imports: [
AvCheckboxImports,
AvDescriptionComponent,
],
template: `<div
av-checkbox
id="select-all"
[indeterminate]="isIndeterminate()"
[(selected)]="isSelected"
(selectedChange)="onSelectedChange($event)"
>
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<span av-checkbox-content>
Select all
<p av-description>Shows indeterminate state (dash icon)</p>
</span>
</div>`,
})
export class CheckboxIndeterminateDemo {
readonly isIndeterminate = signal(true);
readonly isSelected = signal(false);
onSelectedChange(_selected: boolean): void {
this.isIndeterminate.set(false);
}
}Form Integration
Integrate with Angular reactive forms using formControlName on av-checkbox.
import { Component } from '@angular/core';
import { JsonPipe } from '@angular/common';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import {
AvCheckboxImports,
AvLabelComponent,
} from '@avesra/angular';
@Component({
selector: 'app-checkbox-reactive-form-demo',
imports: [JsonPipe, ReactiveFormsModule, AvCheckboxImports, AvLabelComponent],
host: { class: 'flex w-full items-center flex-col gap-3' },
template: `<form class="flex flex-col gap-4" [formGroup]="settingsForm">
<div av-checkbox formControlName="terms">
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<label av-label class="text-sm">Accept terms</label>
</div>
<div av-checkbox formControlName="newsletter">
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<label av-label class="text-sm">Subscribe to newsletter</label>
</div>
<p class="text-sm text-muted">
Form value:
{{ settingsForm.value | json }}
</p>
</form>`,
})
export class CheckboxReactiveFormDemo {
readonly settingsForm = new FormGroup({
terms: new FormControl(false),
newsletter: new FormControl(true),
});
}Reactive Form — Indeterminate
Combine reactive forms with indeterminate state for select-all patterns.
Select-all pattern: parent checkbox reflects child form controls and shows indeterminate when only some permissions are enabled.
import { Component, DestroyRef, inject, signal } from '@angular/core';
import { JsonPipe } from '@angular/common';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { startWith } from 'rxjs';
import {
AvCheckboxImports,
AvDescriptionComponent,
AvLabelComponent,
} from '@avesra/angular';
type PermissionKey = 'read' | 'write' | 'delete';
@Component({
selector: 'app-checkbox-reactive-form-indeterminate-demo',
imports: [JsonPipe, ReactiveFormsModule, AvCheckboxImports, AvLabelComponent, AvDescriptionComponent],
host: { class: 'flex w-full items-center flex-col gap-3' },
template: `<p class="text-sm text-muted">
Select-all pattern: parent checkbox reflects child form controls and shows indeterminate
when only some permissions are enabled.
</p>
<form class="flex flex-col gap-4" [formGroup]="permissionsForm">
<div
av-checkbox
[selected]="selectAllSelected()"
[indeterminate]="selectAllIndeterminate()"
(selectedChange)="onSelectAllChange($event)"
>
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<span av-checkbox-content>
<label av-label class="text-sm font-medium">Select all permissions</label>
<p av-description>Indeterminate when some items are checked</p>
</span>
</div>
<div class="ms-6 flex flex-col gap-3">
<div av-checkbox formControlName="read">
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<label av-label class="text-sm">Read</label>
</div>
<div av-checkbox formControlName="write">
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<label av-label class="text-sm">Write</label>
</div>
<div av-checkbox formControlName="delete">
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<label av-label class="text-sm">Delete</label>
</div>
</div>
<p class="text-sm text-muted">
Form value:
{{ permissionsForm.value | json }}
</p>
<p class="text-sm text-muted">
Select all — selected: {{ selectAllSelected() }}, indeterminate:
{{ selectAllIndeterminate() }}
</p>
</form>`,
})
export class CheckboxReactiveFormIndeterminateDemo {
private readonly destroyRef = inject(DestroyRef);
readonly permissionKeys: PermissionKey[] = ['read', 'write', 'delete'];
readonly permissionsForm = new FormGroup({
read: new FormControl(true),
write: new FormControl(false),
delete: new FormControl(false),
});
readonly selectAllSelected = signal(false);
readonly selectAllIndeterminate = signal(true);
constructor() {
this.permissionsForm.valueChanges
.pipe(startWith(this.permissionsForm.value), takeUntilDestroyed(this.destroyRef))
.subscribe(() => this.syncSelectAllState());
}
onSelectAllChange(selected: boolean): void {
for (const key of this.permissionKeys) {
this.permissionsForm.controls[key].setValue(selected);
}
}
private syncSelectAllState(): void {
const selectedCount = this.permissionKeys.filter(
(key) => this.permissionsForm.controls[key].value,
).length;
this.selectAllSelected.set(selectedCount === this.permissionKeys.length);
this.selectAllIndeterminate.set(
selectedCount > 0 && selectedCount < this.permissionKeys.length,
);
}
}Validation
Drive invalid from form control state and surface errors to the user.
import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import {
AvButtonComponent,
AvCheckboxImports,
AvDescriptionComponent,
AvLabelComponent,
} from '@avesra/angular';
@Component({
selector: 'app-checkbox-validation-demo',
imports: [ReactiveFormsModule, AvCheckboxImports, AvLabelComponent, AvDescriptionComponent, AvButtonComponent],
host: { class: 'flex w-full items-center flex-col gap-3' },
template: `<form class="flex flex-col gap-4" [formGroup]="validationForm" (ngSubmit)="onValidationSubmit()">
<div av-checkbox formControlName="terms" [invalid]="isTermsInvalid()">
<span av-checkbox-control>
<span av-checkbox-indicator></span>
</span>
<span av-checkbox-content>
<label av-label class="text-sm" [invalid]="isTermsInvalid()">Accept terms and conditions</label>
<p av-description>Required to continue</p>
</span>
</div>
@if (isTermsInvalid()) {
<p class="text-sm text-danger">You must accept the terms to continue.</p>
}
<button av-button type="submit">Submit</button>
<p class="text-sm text-muted">
Valid: {{ validationForm.valid }} · Touched: {{ termsControl.touched }}
</p>
</form>`,
})
export class CheckboxValidationDemo {
readonly validationForm = new FormGroup({
terms: new FormControl(false, Validators.requiredTrue),
});
readonly termsControl = this.validationForm.controls.terms;
isTermsInvalid(): boolean {
const control = this.termsControl;
return control.invalid && (control.touched || control.dirty);
}
onValidationSubmit(): void {
this.validationForm.markAllAsTouched();
if (this.validationForm.invalid) {
return;
}
alert('Form submitted successfully!');
}
}Custom Indicator
Project custom SVG content into av-checkbox-indicator to replace the default checkmark.
import { Component } from '@angular/core';
import { AvCheckboxImports } from '@avesra/angular';
@Component({
selector: 'app-checkbox-custom-indicator-demo',
imports: [AvCheckboxImports],
template: `<div class="flex gap-4">
<div av-checkbox default-selected name="heart">
<span av-checkbox-control>
<span av-checkbox-indicator>
<svg fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path
d="M12.62 20.81c-.34.12-.9.12-1.24 0C8.48 19.82 2 15.69 2 8.69 2 5.6 4.49 3.1 7.56 3.1c1.82 0 3.43.88 4.44 2.24a5.53 5.53 0 0 1 4.44-2.24C19.51 3.1 22 5.6 22 8.69c0 7-6.48 11.13-9.38 12.12Z"
fill="currentColor"
/>
</svg>
</span>
</span>
<span av-checkbox-content>Heart</span>
</div>
<div av-checkbox default-selected name="plus">
<span av-checkbox-control>
<span av-checkbox-indicator>
<svg fill="none" viewBox="0 0 24 24" aria-hidden="true">
<path
d="M6 12H18"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="3"
/>
<path
d="M12 18V6"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="3"
/>
</svg>
</span>
</span>
<span av-checkbox-content>Plus</span>
</div>
<div av-checkbox [indeterminate]="true" name="indeterminate">
<span av-checkbox-control>
<span av-checkbox-indicator>
<svg stroke="currentColor" stroke-width="3" viewBox="0 0 24 24" aria-hidden="true">
<line x1="21" x2="3" y1="12" y2="12" />
</svg>
</span>
</span>
<span av-checkbox-content>Indeterminate</span>
</div>
</div>`,
})
export class CheckboxCustomIndicatorDemo {}Styling
CSS Classes
Avesra uses BEM-style classes for predictable customization.
.av-checkbox— Base checkbox layout.av-checkbox--primary— Primary visual variant.av-checkbox--secondary— Secondary visual variant.av-checkbox__control— Checkbox box.av-checkbox__indicator— Checkmark or indeterminate icon.av-checkbox__content— Label and description container.av-checkbox__input— Visually hidden native input
Interactive States
Checkbox styles support both CSS pseudo-classes and data attributes:
- Hover:
:hoveror[data-hovered="true"] - Focus:
:focus-visibleor[data-focus-visible="true"] - Selected:
[data-selected="true"]or[aria-checked="true"] - Indeterminate:
[data-indeterminate="true"] - Pressed:
:activeor[data-pressed="true"] - Disabled:
:disabled,[data-disabled="true"], or[aria-disabled="true"] - Invalid:
[data-invalid="true"]or[aria-invalid="true"]
Accessibility
Checkbox uses a visually hidden native <input type="checkbox"> for semantics and provides:
aria-checkedreflecting checked, unchecked, and indeterminate states- Keyboard activation via Space when focused
- Label association through adjacent
av-labeloraria-label aria-invalidandaria-describedbyfor validation feedback- Disabled state communicated via
disabledandaria-disabled
API
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'primary' | 'secondary' | 'primary' | Visual style variant. Inherits from `av-checkbox-group` when omitted. |
disabled | boolean | false | Disables interaction. Also set by reactive forms or parent group. |
invalid | boolean | false | Marks the checkbox as invalid. Inherits from parent group when omitted. |
default-selected | boolean | false | Initial selected state for uncontrolled standalone usage. |
selected | boolean | false | Selected state for standalone usage. Supports two-way binding with `[(selected)]`. |
selectedChange | EventEmitter<boolean> | — | Emits when the selected state changes. |
indeterminate | boolean | false | Indeterminate (partially selected) state. |
aria-label | string | — | Accessible label when no visible label is provided. |
name | string | — | Form field name for native form submission. |
value | string | — | Option value when used inside `av-checkbox-group`. Required in groups. |