Form
Switch
Usage#
import { WdsSwitchComponent } from '@wds/angular/switch';Examples#
Default#
import { Component } from '@angular/core';
import { WdsSwitchComponent } from '@wds/angular/switch';
@Component({
selector: 'app-switch-default-demo',
imports: [WdsSwitchComponent],
template: `
<wds-switch>Airplane Mode</wds-switch>
`,
})
export class SwitchDefaultDemo {}Description#
description slot adds helper text below the label.
import { Component } from '@angular/core';
import { WdsSwitchComponent } from '@wds/angular/switch';
@Component({
selector: 'app-switch-description-demo',
imports: [WdsSwitchComponent],
template: `
<div class="flex w-full max-w-sm flex-col">
<wds-switch>
Share across devices
<span slot="description">Focus is shared across devices, and turns off when you leave the app.</span>
</wds-switch>
</div>
`,
})
export class SwitchDescriptionDemo {}Choice card#
variant="card" turns the switch into a card: a click anywhere on it toggles the switch. wds-change bubbles, so one listener on a container can handle a group of switches.
sync: false, push: true
import { Component, signal } from '@angular/core';
import { FormField, form } from '@angular/forms/signals';
import { WdsCheckedField } from '@wds/angular/forms';
import { WdsSwitchComponent } from '@wds/angular/switch';
@Component({
selector: 'app-switch-choice-card-demo',
imports: [FormField, WdsSwitchComponent, WdsCheckedField],
template: `
<div class="flex w-sm max-w-full flex-col gap-3">
<wds-switch variant="card" [formField]="settingsForm.sync">
Sync
<span slot="description">Keep your settings in sync across all your devices.</span>
</wds-switch>
<wds-switch variant="card" [formField]="settingsForm.push">
Push notifications
<span slot="description">Get notified about new messages.</span>
</wds-switch>
<p class="m-0 text-sm text-muted-foreground">sync: {{ settings().sync }}, push: {{ settings().push }}</p>
</div>
`,
})
export class SwitchChoiceCardDemo {
protected readonly settings = signal({ sync: false, push: true });
protected readonly settingsForm = form(this.settings);
}Disabled#
import { Component } from '@angular/core';
import { WdsSwitchComponent } from '@wds/angular/switch';
@Component({
selector: 'app-switch-disabled-demo',
imports: [WdsSwitchComponent],
template: `
<div class="flex flex-col gap-3">
<wds-switch disabled>Disabled</wds-switch>
<wds-switch disabled checked>Disabled, on</wds-switch>
</div>
`,
})
export class SwitchDisabledDemo {}Invalid#
invalid shows the error state and sets aria-invalid.
import { Component } from '@angular/core';
import { WdsSwitchComponent } from '@wds/angular/switch';
@Component({
selector: 'app-switch-invalid-demo',
imports: [WdsSwitchComponent],
template: `
<wds-switch invalid>Accept terms and conditions</wds-switch>
`,
})
export class SwitchInvalidDemo {}Size#
import { Component } from '@angular/core';
import { WdsSwitchComponent } from '@wds/angular/switch';
@Component({
selector: 'app-switch-size-demo',
imports: [WdsSwitchComponent],
template: `
<div class="flex flex-col gap-3">
<wds-switch size="sm">Small</wds-switch>
<wds-switch>Default</wds-switch>
</div>
`,
})
export class SwitchSizeDemo {}Forms#
Integration with Angular forms. Pick the API your app uses.
Signal forms#
WdsSwitchField connects the switch to [formField]. The form controls checked, disabled, invalid and required: set them in the schema (here disabled()), not as attributes.
import { Component, signal } from '@angular/core';
import { FormField, FormRoot, disabled, form } from '@angular/forms/signals';
import { WdsButtonComponent } from '@wds/angular/button';
import { WdsCheckedField } from '@wds/angular/forms';
import { WdsSwitchComponent } from '@wds/angular/switch';
interface Notifications {
marketing: boolean;
security: boolean;
}
@Component({
selector: 'app-switch-signal-forms-demo',
imports: [FormField, FormRoot, WdsButtonComponent, WdsSwitchComponent, WdsCheckedField],
template: `
<form class="flex w-sm max-w-full flex-col gap-3" [formRoot]="notificationsForm">
<wds-switch variant="card" [formField]="notificationsForm.marketing">
Marketing emails
<span slot="description">Receive emails about new products, features, and more.</span>
</wds-switch>
<wds-switch variant="card" [formField]="notificationsForm.security">
Security emails
<span slot="description">Receive emails about your account security.</span>
</wds-switch>
<wds-button type="submit" class="self-start">Submit</wds-button>
<p class="m-0 font-mono text-sm text-muted-foreground">Submitted: {{ submitted() }}</p>
</form>
`,
})
export class SwitchSignalFormsDemo {
protected readonly notifications = signal<Notifications>({ marketing: false, security: true });
protected readonly submitted = signal('—');
protected readonly notificationsForm = form(
this.notifications,
(path) => {
// Security emails are always on.
disabled(path.security);
},
{
submission: {
action: async (form) => {
this.submitted.set(JSON.stringify(form().value()));
},
},
}
);
}Template-driven#
WdsSwitchValueAccessor makes ngModel work on wds-switch. Inside a <form> each switch needs a name.
import { Component, signal } from '@angular/core';
import { FormsModule, type NgForm } from '@angular/forms';
import { WdsButtonComponent } from '@wds/angular/button';
import { WdsCheckedValueAccessor } from '@wds/angular/forms';
import { WdsSwitchComponent } from '@wds/angular/switch';
@Component({
selector: 'app-switch-template-driven-demo',
imports: [FormsModule, WdsButtonComponent, WdsSwitchComponent, WdsCheckedValueAccessor],
template: `
<form #notificationsForm="ngForm" class="flex w-sm max-w-full flex-col gap-3" (ngSubmit)="save(notificationsForm)">
<wds-switch variant="card" name="marketing" [(ngModel)]="notifications.marketing">
Marketing emails
<span slot="description">Receive emails about new products, features, and more.</span>
</wds-switch>
<!-- Security emails are always on. -->
<wds-switch variant="card" name="security" [(ngModel)]="notifications.security" disabled>
Security emails
<span slot="description">Receive emails about your account security.</span>
</wds-switch>
<wds-button type="submit" class="self-start">Submit</wds-button>
<p class="m-0 font-mono text-sm text-muted-foreground">Submitted: {{ submitted() }}</p>
</form>
`,
})
export class SwitchTemplateDrivenDemo {
protected readonly notifications = { marketing: false, security: true };
protected readonly submitted = signal('—');
protected save(form: NgForm): void {
// `value` skips disabled controls; `getRawValue()` includes them.
this.submitted.set(JSON.stringify(form.form.getRawValue()));
}
}Reactive forms#
WdsSwitchValueAccessor works with formControl and formControlName. Disable a control in the FormControl, not with the disabled attribute.
import { Component, signal } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { WdsButtonComponent } from '@wds/angular/button';
import { WdsCheckedValueAccessor } from '@wds/angular/forms';
import { WdsSwitchComponent } from '@wds/angular/switch';
@Component({
selector: 'app-switch-reactive-forms-demo',
imports: [ReactiveFormsModule, WdsButtonComponent, WdsSwitchComponent, WdsCheckedValueAccessor],
template: `
<form class="flex w-sm max-w-full flex-col gap-3" [formGroup]="notificationsForm" (ngSubmit)="save()">
<wds-switch variant="card" formControlName="marketing">
Marketing emails
<span slot="description">Receive emails about new products, features, and more.</span>
</wds-switch>
<wds-switch variant="card" formControlName="security">
Security emails
<span slot="description">Receive emails about your account security.</span>
</wds-switch>
<wds-button type="submit" class="self-start">Submit</wds-button>
<p class="m-0 font-mono text-sm text-muted-foreground">Submitted: {{ submitted() }}</p>
</form>
`,
})
export class SwitchReactiveFormsDemo {
protected readonly notificationsForm = new FormGroup({
marketing: new FormControl(false, { nonNullable: true }),
// Security emails are always on: the control is disabled in the form, not with an attribute.
security: new FormControl({ value: true, disabled: true }, { nonNullable: true }),
});
protected readonly submitted = signal('—');
protected save(): void {
// `value` skips disabled controls; `getRawValue()` includes them.
this.submitted.set(JSON.stringify(this.notificationsForm.getRawValue()));
}
}API References#
wds-switch#
<input type="checkbox" role="switch">.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| size | WdsSwitchSize | 'default' | |
| checked | boolean | false | wds-change. |
| disabled | boolean | false | |
| invalid | boolean | false | aria-invalid. |
| required | boolean | false | |
| name | string | '' | |
| value | string | 'on' | |
| variant | WdsToggleVariant | 'default' | card is a clickable card with a border and the control on the right. |
Events
| Event | Description |
|---|---|
| wds-changeCustomEvent<WdsToggleChangeDetail> |
Methods
| Method | Description |
|---|---|
| formResetCallback() | |
| formDisabledCallback(disabled: boolean) | |
| checkValidity() | true when the value is valid; otherwise fires invalid on the element. |
| reportValidity() | checkValidity(), and shows the browser validation bubble when the value is invalid. |
| setCustomValidity(message: string) | input.setCustomValidity(). |
Slots
| Slot | Description |
|---|---|
| (default) | |
| description | aria-describedby). |
CSS parts (::part)
| Part | Description |
|---|---|
| root | card variant). |
| control | <input>). |
| thumb | |
| label | |
| description |
CSS custom properties
| Property | Description |
|---|---|
| --wds-switch-track | |
| --wds-switch-checked | |
| --wds-switch-thumb | |
| --wds-switch-thumb-checked |
Accessibility#
Screen reader#
wds-switch renders a native <input type="checkbox" role="switch">, so screen readers announce it as a switch with its on or off state.
<label> of the input and the description slot is linked with aria-describedby. Without visible text give the switch an aria-label: it is forwarded to the input.
invalid sets aria-invalid="true" and required sets aria-required="true"; disabled disables the native input.
value (default on) is submitted under name.
Keyboard support#
| Key | Function |
|---|---|