AvesraAvesrabeta

Checkbox Group

Group of checkboxes with shared value, validation, and variant styling.

Import

import { AvCheckboxGroupImports } from '@avesra/angular';

Usage

Choose all that apply

import { Component } from '@angular/core';
import {
  AvCheckboxGroupImports,
  AvDescriptionComponent,
  AvLabelComponent,
} from '@avesra/angular';

@Component({
  selector: 'app-checkbox-group-basic-demo',
  imports: [
    AvCheckboxGroupImports,
    AvLabelComponent,
    AvDescriptionComponent,
  ],
  template: `<av-checkbox-group name="interests">
      <label av-label>Select your interests</label>
      <p av-description>Choose all that apply</p>
      <div av-checkbox value="coding">
        <span av-checkbox-control>
          <span av-checkbox-indicator></span>
        </span>
        <span av-checkbox-content>
          Coding
          <p av-description>Love building software</p>
        </span>
      </div>
      <div av-checkbox value="design">
        <span av-checkbox-control>
          <span av-checkbox-indicator></span>
        </span>
        <span av-checkbox-content>
          Design
          <p av-description>Enjoy creating beautiful interfaces</p>
        </span>
      </div>
      <div av-checkbox value="writing">
        <span av-checkbox-control>
          <span av-checkbox-indicator></span>
        </span>
        <span av-checkbox-content>
          Writing
          <p av-description>Passionate about content creation</p>
        </span>
      </div>
    </av-checkbox-group>`,
})
export class CheckboxGroupBasicDemo {}

Anatomy

Import the CheckboxGroup components and compose the parts with av-checkbox-group, av-checkbox, av-checkbox-control, av-checkbox-indicator, and av-checkbox-content.

<av-checkbox-group name="interests">
  <label av-label></label>
  <p av-description></p> <!-- Optional -->
  <div av-checkbox value="option1">
    <span av-checkbox-control>
      <span av-checkbox-indicator></span>
    </span>
    <span av-checkbox-content>
      Label <!-- plain text — the clickable label -->
    </span>
    <p av-description></p> <!-- Optional per-checkbox help text -->
  </div>
  <p av-error-message></p> <!-- Optional -->
</av-checkbox-group>

In Surface

When used inside a av-surface component, use variant="secondary" to apply the lower emphasis variant suitable for surface backgrounds.

Choose all that apply

import { Component } from '@angular/core';
import {
  AvCheckboxGroupImports,
  AvDescriptionComponent,
  AvLabelComponent,
  AvSurfaceComponent,
} from '@avesra/angular';

@Component({
  selector: 'app-checkbox-group-on-surface-demo',
  imports: [
    AvSurfaceComponent,
    AvCheckboxGroupImports,
    AvLabelComponent,
    AvDescriptionComponent,
  ],
  template: `<div av-surface class="w-full rounded-3xl p-6">
      <av-checkbox-group name="interests" variant="secondary">
        <label av-label>Select your interests</label>
        <p av-description>Choose all that apply</p>
        <div av-checkbox value="coding">
          <span av-checkbox-control>
            <span av-checkbox-indicator></span>
          </span>
          <span av-checkbox-content>
            Coding
            <p av-description>Love building software</p>
          </span>
        </div>
        <div av-checkbox value="design">
          <span av-checkbox-control>
            <span av-checkbox-indicator></span>
          </span>
          <span av-checkbox-content>
            Design
            <p av-description>Enjoy creating beautiful interfaces</p>
          </span>
        </div>
        <div av-checkbox value="writing">
          <span av-checkbox-control>
            <span av-checkbox-indicator></span>
          </span>
          <span av-checkbox-content>
            Writing
            <p av-description>Passionate about content creation</p>
          </span>
        </div>
      </av-checkbox-group>
    </div>`,
})
export class CheckboxGroupOnSurfaceDemo {}

Disabled

Feature selection is temporarily disabled

import { Component } from '@angular/core';
import {
  AvCheckboxGroupImports,
  AvDescriptionComponent,
  AvLabelComponent,
} from '@avesra/angular';

@Component({
  selector: 'app-checkbox-group-disabled-demo',
  imports: [
    AvCheckboxGroupImports,
    AvLabelComponent,
    AvDescriptionComponent,
  ],
  template: `<av-checkbox-group disabled name="disabled-features">
      <label av-label>Features</label>
      <p av-description>Feature selection is temporarily disabled</p>
      <div av-checkbox value="feature1">
        <span av-checkbox-control>
          <span av-checkbox-indicator></span>
        </span>
        <span av-checkbox-content>
          Feature 1
          <p av-description>This feature is coming soon</p>
        </span>
      </div>
      <div av-checkbox value="feature2">
        <span av-checkbox-control>
          <span av-checkbox-indicator></span>
        </span>
        <span av-checkbox-content>
          Feature 2
          <p av-description>This feature is coming soon</p>
        </span>
      </div>
    </av-checkbox-group>`,
})
export class CheckboxGroupDisabledDemo {}

Indeterminate

import { Component, computed, signal } from '@angular/core';
import { AvCheckboxGroupImports } from '@avesra/angular';

@Component({
  selector: 'app-checkbox-group-indeterminate-demo',
  imports: [AvCheckboxGroupImports],
  template: `<div>
      <div
        av-checkbox
        name="select-all"
        [selected]="selectAllSelected()"
        [indeterminate]="selectAllIndeterminate()"
        (selectedChange)="onSelectAllChange($event)"
      >
        <span av-checkbox-control>
          <span av-checkbox-indicator></span>
        </span>
        <span av-checkbox-content>Select all</span>
      </div>
      <div class="ms-6 flex flex-col gap-2">
        <av-checkbox-group [(value)]="selected">
          <div av-checkbox value="coding">
            <span av-checkbox-control>
              <span av-checkbox-indicator></span>
            </span>
            <span av-checkbox-content>Coding</span>
          </div>
          <div av-checkbox value="design">
            <span av-checkbox-control>
              <span av-checkbox-indicator></span>
            </span>
            <span av-checkbox-content>Design</span>
          </div>
          <div av-checkbox value="writing">
            <span av-checkbox-control>
              <span av-checkbox-indicator></span>
            </span>
            <span av-checkbox-content>Writing</span>
          </div>
        </av-checkbox-group>
      </div>
    </div>`,
})
export class CheckboxGroupIndeterminateDemo {
  readonly allOptions = ['coding', 'design', 'writing'] as const;
  readonly selected = signal<string[]>(['coding']);

  readonly selectAllSelected = computed(
    () => this.selected().length === this.allOptions.length,
  );

  readonly selectAllIndeterminate = computed(() => {
    const length = this.selected().length;
    return length > 0 && length < this.allOptions.length;
  });

  onSelectAllChange(isSelected: boolean): void {
    this.selected.set(isSelected ? [...this.allOptions] : []);
  }
}

Controlled

import { Component, signal } from '@angular/core';
import {
  AvCheckboxGroupImports,
  AvLabelComponent,
} from '@avesra/angular';

@Component({
  selector: 'app-checkbox-group-controlled-demo',
  imports: [
    AvCheckboxGroupImports,
    AvLabelComponent,
  ],
  template: `<av-checkbox-group
      class="min-w-[320px]"
      name="skills"
      [(value)]="selected"
    >
      <label av-label>Your skills</label>
      <div av-checkbox value="coding">
        <span av-checkbox-control>
          <span av-checkbox-indicator></span>
        </span>
        <span av-checkbox-content>Coding</span>
      </div>
      <div av-checkbox value="design">
        <span av-checkbox-control>
          <span av-checkbox-indicator></span>
        </span>
        <span av-checkbox-content>Design</span>
      </div>
      <div av-checkbox value="writing">
        <span av-checkbox-control>
          <span av-checkbox-indicator></span>
        </span>
        <span av-checkbox-content>Writing</span>
      </div>
      <label av-label class="my-4 text-sm text-muted">
        Selected: {{ selected().join(', ') || 'None' }}
      </label>
    </av-checkbox-group>`,
})
export class CheckboxGroupControlledDemo {
  readonly selected = signal<string[]>(['coding', 'design']);
}

Validation

import { Component, computed, signal } from '@angular/core';
import {
  AvButtonComponent,
  AvCheckboxGroupImports,
  AvFieldErrorComponent,
  AvFormComponent,
  AvLabelComponent,
} from '@avesra/angular';

@Component({
  selector: 'app-checkbox-group-validation-demo',
  imports: [
    AvFormComponent,
    AvCheckboxGroupImports,
    AvLabelComponent,
    AvFieldErrorComponent,
    AvButtonComponent,
  ],
  template: `<form
      av-form
      class="flex flex-col gap-4 px-4"
      (submit)="onSubmit($event)"
    >
      <av-checkbox-group
        name="preferences"
        [(value)]="selected"
        [invalid]="isInvalid()"
      >
        <label av-label required [invalid]="isInvalid()">Preferences</label>
        <div av-checkbox name="preferences" value="email">
          <span av-checkbox-control>
            <span av-checkbox-indicator></span>
          </span>
          <span av-checkbox-content>Email notifications</span>
        </div>
        <div av-checkbox name="preferences" value="sms">
          <span av-checkbox-control>
            <span av-checkbox-indicator></span>
          </span>
          <span av-checkbox-content>SMS notifications</span>
        </div>
        <div av-checkbox name="preferences" value="push">
          <span av-checkbox-control>
            <span av-checkbox-indicator></span>
          </span>
          <span av-checkbox-content>Push notifications</span>
        </div>
        <p av-field-error [visible]="isInvalid()">
          Please select at least one notification method.
        </p>
      </av-checkbox-group>
      <button av-button type="submit">Submit</button>
    </form>`,
})
export class CheckboxGroupValidationDemo {
  readonly selected = signal<string[]>([]);
  readonly submitted = signal(false);

  readonly isInvalid = computed(
    () => this.submitted() && this.selected().length === 0,
  );

  onSubmit(event: Event): void {
    event.preventDefault();
    this.submitted.set(true);

    if (this.selected().length === 0) {
      return;
    }

    const form = event.target as HTMLFormElement;
    const formData = new FormData(form);
    const values = formData.getAll('preferences');

    alert(`Selected preferences: ${values.join(', ')}`);
  }
}

Features and Add-ons Example

Choose how you want to receive updates

import { Component } from '@angular/core';
import {
  AvCheckboxGroupImports,
  AvDescriptionComponent,
  AvLabelComponent,
} from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';

@Component({
  selector: 'app-checkbox-group-features-and-addons-demo',
  imports: [
    AvCheckboxGroupImports,
    AvLabelComponent,
    AvDescriptionComponent,
    AppIconComponent,
  ],
  template: `<div class="flex w-full flex-col items-center gap-10 px-4 py-8">
      <section class="flex w-full min-w-[320px] flex-col gap-4">
        <av-checkbox-group name="notification-preferences">
          <label av-label>Notification preferences</label>
          <p av-description>Choose how you want to receive updates</p>
          <div class="flex flex-col gap-2">
            @for (addon of addOns; track addon.value) {
              <div av-checkbox [value]="addon.value" variant="secondary" class="group w-full">
                <div
                  class="relative flex w-full flex-row items-start justify-start gap-4 rounded-3xl bg-surface px-5 py-4 transition-all group-data-[selected=true]:bg-accent/10"
                >
                  <span
                    av-checkbox-control
                    class="absolute top-3 end-4 size-5 rounded-full before:rounded-full"
                  >
                    <span av-checkbox-indicator></span>
                  </span>
                  <app-icon
                    [icon]="addon.icon"
                    size="20"
                    class="size-5 text-accent-soft-foreground"
                  />
                  <span av-checkbox-content class="flex flex-col gap-1">
                    <span>{{ addon.title }}</span>
                    <p av-description>{{ addon.description }}</p>
                  </span>
                </div>
              </div>
            }
          </div>
        </av-checkbox-group>
      </section>
    </div>`,
})
export class CheckboxGroupFeaturesAndAddOnsDemo {
  readonly addOns = [
    {
      description: 'Receive updates via email',
      icon: 'solar:letter-linear',
      title: 'Email Notifications',
      value: 'email',
    },
    {
      description: 'Get instant SMS notifications',
      icon: 'solar:chat-round-line-linear',
      title: 'SMS Alerts',
      value: 'sms',
    },
    {
      description: 'Browser and mobile push alerts',
      icon: 'solar:bell-linear',
      title: 'Push Notifications',
      value: 'push',
    },
  ];
}

With Custom Indicator

Select the features you want

import { Component, signal } from '@angular/core';
import {
  AvCheckboxGroupImports,
  AvDescriptionComponent,
  AvLabelComponent,
} from '@avesra/angular';

@Component({
  selector: 'app-checkbox-group-with-custom-indicator-demo',
  imports: [
    AvCheckboxGroupImports,
    AvLabelComponent,
    AvDescriptionComponent,
  ],
  template: `<av-checkbox-group name="features" [(value)]="selected">
      <label av-label>Features</label>
      <p av-description>Select the features you want</p>
      <div av-checkbox value="notifications">
        <span av-checkbox-control>
          <span av-checkbox-indicator>
            @if (selected().includes('notifications')) {
              <svg
                aria-hidden="true"
                fill="none"
                stroke="currentColor"
                stroke-linecap="round"
                stroke-width="2"
                viewBox="0 0 24 24"
              >
                <path d="M6 18L18 6M6 6l12 12" />
              </svg>
            }
          </span>
        </span>
        <span av-checkbox-content>
          Email notifications
          <p av-description>Receive updates via email</p>
        </span>
      </div>
      <div av-checkbox value="newsletter">
        <span av-checkbox-control>
          <span av-checkbox-indicator>
            @if (selected().includes('newsletter')) {
              <svg
                aria-hidden="true"
                fill="none"
                stroke="currentColor"
                stroke-linecap="round"
                stroke-width="2"
                viewBox="0 0 24 24"
              >
                <path d="M6 18L18 6M6 6l12 12" />
              </svg>
            }
          </span>
        </span>
        <span av-checkbox-content>
          Newsletter
          <p av-description>Get weekly newsletters</p>
        </span>
      </div>
    </av-checkbox-group>`,
})
export class CheckboxGroupWithCustomIndicatorDemo {
  readonly selected = signal<string[]>([]);
}

Reactive form

Integrate with Angular reactive forms using [formControl] or formControlName on av-checkbox-group.

import { Component } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import {
  AvCheckboxGroupImports,
  AvLabelComponent,
} from '@avesra/angular';

@Component({
  selector: 'app-checkbox-group-reactive-form-demo',
  imports: [
    ReactiveFormsModule,
    AvCheckboxGroupImports,
    AvLabelComponent,
  ],
  template: `<av-checkbox-group class="min-w-[320px]" [formControl]="interestsControl">
      <label av-label>Interests</label>
      <div av-checkbox value="coding">
        <span av-checkbox-control>
          <span av-checkbox-indicator></span>
        </span>
        <span av-checkbox-content>Coding</span>
      </div>
      <div av-checkbox value="design">
        <span av-checkbox-control>
          <span av-checkbox-indicator></span>
        </span>
        <span av-checkbox-content>Design</span>
      </div>
      <div av-checkbox value="writing">
        <span av-checkbox-control>
          <span av-checkbox-indicator></span>
        </span>
        <span av-checkbox-content>Writing</span>
      </div>
      <label av-label class="my-4 text-sm text-muted">
        Form value: {{ interestsControl.value?.join(', ') || 'None' }}
      </label>
    </av-checkbox-group>`,
})
export class CheckboxGroupReactiveFormDemo {
  readonly interestsControl = new FormControl<string[]>(['design']);
}

Styling

CSS Classes

Avesra uses BEM-style classes for predictable customization.

  • .av-checkbox-group — Group layout container
  • .av-checkbox-group--primary — Primary visual variant
  • .av-checkbox-group--secondary — Secondary visual variant (inherited by children)

Interactive States

Group-level disabled and invalid props cascade to child checkboxes. Individual checkbox states use the same selectors documented on the Checkbox page.

Accessibility

Checkbox Group renders a role="group" container and provides:

  • Group labeling via av-label with aria-labelledby
  • Supplementary text via av-description linked with aria-describedby
  • Each child checkbox retains independent aria-checked state
  • Group-level aria-invalid when validation fails
  • Keyboard navigation between checkboxes via Tab

API

PropTypeDefaultDescription
variant'primary' | 'secondary''primary'Shared visual variant applied to all child checkboxes.
disabledbooleanfalseDisables all child checkboxes.
invalidbooleanfalseMarks all child checkboxes as invalid.
namestring—Form field name for native form submission.
default-valuestring[][]Initial selected values for uncontrolled usage.
valuestring[][]Selected values. Supports two-way binding with `[(value)]`.
valueChangeEventEmitter<string[]>—Emits when the selected values change.

Made with ❤ by SyntaxHertz. Open source and available on GitHub.