Checkbox
A checkbox with an indeterminate state that works with Angular forms.
import { … } from '@inlaylab/ivorie/checkbox'You agree to our Terms of Service and Privacy Policy.
Get notified about sign-ins and security changes.
New features, product updates and occasional offers.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { IvCheckbox } from '@inlaylab/ivorie/checkbox';
@Component({
selector: 'demo-checkbox',
imports: [IvCheckbox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="flex w-full max-w-sm flex-col gap-4 text-sm">
<div class="flex items-start gap-3">
<iv-checkbox id="cb-terms" [(checked)]="terms" class="mt-0.5" />
<div class="grid gap-1">
<label for="cb-terms" class="cursor-pointer font-medium">Accept terms and conditions</label>
<p class="text-muted-foreground">You agree to our Terms of Service and Privacy Policy.</p>
</div>
</div>
<div class="flex items-start gap-3">
<iv-checkbox id="cb-activity" [(checked)]="activity" class="mt-0.5" />
<div class="grid gap-1">
<label for="cb-activity" class="cursor-pointer font-medium">Account activity</label>
<p class="text-muted-foreground">Get notified about sign-ins and security changes.</p>
</div>
</div>
<div class="flex items-start gap-3">
<iv-checkbox id="cb-marketing" [(checked)]="marketing" class="mt-0.5" />
<div class="grid gap-1">
<label for="cb-marketing" class="cursor-pointer font-medium">Marketing emails</label>
<p class="text-muted-foreground">New features, product updates and occasional offers.</p>
</div>
</div>
</div>
`,
})
export default class CheckboxDemo {
protected readonly terms = signal(false);
protected readonly activity = signal(true);
protected readonly marketing = signal(false);
}
Installation
npx ivorie add checkboximport { … } from '@inlaylab/ivorie/checkbox'A native <button role="checkbox"> that works as a form control. It supports ngModel, reactive forms and plain two-way binding.
Usage
import { IvCheckbox } from '@inlaylab/ivorie/checkbox';<iv-checkbox [(checked)]="agreed" aria-label="Accept terms" />Examples
With ngModel
Value: false
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { IvCheckbox } from '@inlaylab/ivorie/checkbox';
@Component({
selector: 'demo-checkbox-ngmodel',
imports: [FormsModule, IvCheckbox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="flex flex-col items-center gap-3">
<iv-checkbox [(ngModel)]="agreed" aria-label="Accept terms" />
<p class="text-muted-foreground text-sm">Value: {{ agreed() }}</p>
</div>
`,
})
export default class CheckboxNgmodelDemo {
protected readonly agreed = signal(false);
}
Reactive forms
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { IvCheckbox } from '@inlaylab/ivorie/checkbox';
@Component({
selector: 'demo-checkbox-reactive-forms',
imports: [ReactiveFormsModule, IvCheckbox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<form [formGroup]="form" class="flex flex-col items-center gap-3">
<iv-checkbox formControlName="terms" aria-label="Accept terms" />
<p class="text-muted-foreground text-sm">Value: {{ form.value.terms }}</p>
</form>
`,
})
export default class CheckboxReactiveFormsDemo {
protected readonly form = new FormGroup({ terms: new FormControl(false) });
}
With label
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { IvCheckbox } from '@inlaylab/ivorie/checkbox';
@Component({
selector: 'demo-checkbox-with-label',
imports: [IvCheckbox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<label class="flex cursor-pointer items-center gap-2 text-sm">
<iv-checkbox [(checked)]="remember" />
Remember me
</label>
`,
})
export default class CheckboxWithLabelDemo {
protected readonly remember = signal(true);
}
Indeterminate
import { ChangeDetectionStrategy, Component, computed, signal } from '@angular/core';
import { IvCheckbox } from '@inlaylab/ivorie/checkbox';
@Component({
selector: 'demo-checkbox-indeterminate',
imports: [IvCheckbox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="flex flex-col gap-2 text-sm">
<label class="flex cursor-pointer items-center gap-2 font-medium">
<iv-checkbox [checked]="all()" [indeterminate]="some()" (checkedChange)="toggleAll($event)" />
Select all
</label>
@for (item of items; track item) {
<label class="flex cursor-pointer items-center gap-2 pl-6">
<iv-checkbox [checked]="selected().includes(item)" (checkedChange)="toggle(item)" />
{{ item }}
</label>
}
</div>
`,
})
export default class CheckboxIndeterminateDemo {
protected readonly items = ['Email', 'SMS', 'Push'];
protected readonly selected = signal<string[]>(['Email']);
protected readonly all = computed(() => this.selected().length === this.items.length);
protected readonly some = computed(() => this.selected().length > 0 && !this.all());
protected toggleAll(checked: boolean) {
this.selected.set(checked ? [...this.items] : []);
}
protected toggle(item: string) {
this.selected.update((s) => (s.includes(item) ? s.filter((i) => i !== item) : [...s, item]));
}
}
Disabled
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { IvCheckbox } from '@inlaylab/ivorie/checkbox';
@Component({
selector: 'demo-checkbox-disabled',
imports: [IvCheckbox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="flex flex-col gap-3 text-sm">
<label class="flex items-center gap-2">
<iv-checkbox [disabled]="true" />
Unchecked
</label>
<label class="flex items-center gap-2">
<iv-checkbox [checked]="true" [disabled]="true" />
Checked
</label>
</div>
`,
})
export default class CheckboxDisabledDemo {}
API
Every part also accepts class.
iv-checkbox
| Name | Type | Default | Description |
|---|---|---|---|
checked |
boolean |
false |
Checked state. Bind with [(checked)]. |
indeterminate |
boolean |
false |
Shows a minus icon. A click moves it to checked. |
disabled |
boolean |
false |
Blocks interaction. Also disabled when the form control is disabled. |
id |
string | null |
null |
Set on the inner button, so <label for> works. |
aria-label |
string | null |
null |
Set on the inner button. |
aria-labelledby |
string | null |
null |
Set on the inner button. |
Outputs:
| Name | Payload | Description |
|---|---|---|
checkedChange |
boolean |
Emits after the user toggles. |
Accessibility
- Renders
<button type="button" role="checkbox">. The host isdisplay: contents. aria-checkedistrue,falseor"mixed"(indeterminate).Spacetoggles.Enterdoes nothing, as in Radix.Tabmoves focus.- A wrapping
<label>and<label for>both work, because the button is labelable. - Disabled uses the native
disabledattribute. - An invisible padded area makes the hit target larger.
Notes
ngModelandformControlNameneedFormsModuleorReactiveFormsModule.- The button has
data-stateset tochecked,uncheckedorindeterminate. - The form marks the control touched on blur. Setting the value from the form does not emit
checkedChange. - Indeterminate click cycle: indeterminate, checked, unchecked, checked.