Create

Frameworks

Angular

Install and use WDS in an Angular app.

Installation#

Create project#

Skip this step if you already have an Angular app.

bash
ng new my-app

Install packages#

@wds/angular needs @wds/core, @wds/tokens and lit.

pnpmnpmyarnbun
pnpm add @wds/angular @wds/core @wds/tokens lit
npm install @wds/angular @wds/core @wds/tokens lit
yarn add @wds/angular @wds/core @wds/tokens lit
bun add @wds/angular @wds/core @wds/tokens lit

Until the packages are in a registry, install them from files: see Local installation.

Add the tokens#

Import the CSS variables once, in the global styles.

styles.css
/* src/styles.css */
@import '@wds/tokens/tokens.css';

/* Optional with SSR/SSG: hides elements until they are registered. */
wds-radio-group:not(:defined) {
  visibility: hidden;
}

Use components#

Import a wrapper from its entry point, such as @wds/angular/button. Wrappers have typed inputs (booleanAttribute for flags) and outputs: the wds-change event is the (wdsChange) output, and $event is its detail.

app.ts
import { Component, signal } from '@angular/core';
// Every component has its own entry point, so the bundle gets only what you use.
import { WdsButtonComponent } from '@wds/angular/button';
import { WdsCardComponent } from '@wds/angular/card';
import { WDS_RADIO } from '@wds/angular/radio';

@Component({
  selector: 'app-root',
  imports: [WdsButtonComponent, WdsCardComponent, ...WDS_RADIO],
  template: `
    <wds-card heading="Plan">
      <wds-radio-group [value]="plan()" (wdsChange)="plan.set($event.value)">
        <wds-radio value="free">Free</wds-radio>
        <wds-radio value="pro">Pro</wds-radio>
      </wds-radio-group>
      <wds-button slot="footer" [loading]="saving()" (click)="save()">Save</wds-button>
    </wds-card>
  `,
})
export class App {
  protected readonly plan = signal('free');
  protected readonly saving = signal(false);

  protected save() {
    this.saving.set(true);
  }
}

Forms#

@wds/angular/forms connects form controls to Angular forms. Each control page has a Forms section with Signal Forms, template-driven and Reactive Forms examples, for example Input.

Signal Forms#

The form sets disabled, required and validation on the control; wds-field-error shows the errors.

signup-form.ts
import { Component, signal } from '@angular/core';
import { FormField, form, required } from '@angular/forms/signals';
import { WDS_FIELD } from '@wds/angular/field';
import { WdsTextField } from '@wds/angular/forms';
import { WdsInputComponent } from '@wds/angular/input';

@Component({
  selector: 'app-signup-form',
  imports: [...WDS_FIELD, FormField, WdsInputComponent, WdsTextField],
  template: `
    <wds-field>
      <wds-field-label>Email</wds-field-label>
      <wds-input type="email" [formField]="signup.email" />
      <wds-field-error [errors]="signup.email().errors()" />
    </wds-field>
  `,
})
export class SignupForm {
  readonly model = signal({ email: '' });
  readonly signup = form(this.model, (p) => required(p.email, { message: 'Enter your email.' }));
}

Reactive Forms#

signup-form.ts
import { Component } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { WdsCheckboxComponent } from '@wds/angular/checkbox';
import { WdsCheckedValueAccessor, WdsTextValueAccessor } from '@wds/angular/forms';
import { WdsInputComponent } from '@wds/angular/input';

@Component({
  selector: 'app-signup-form',
  imports: [ReactiveFormsModule, WdsCheckboxComponent, WdsInputComponent, WdsCheckedValueAccessor, WdsTextValueAccessor],
  template: `
    <wds-input type="email" [formControl]="email" />
    <wds-checkbox [formControl]="terms">I accept the terms</wds-checkbox>
  `,
})
export class SignupForm {
  readonly email = new FormControl('');
  readonly terms = new FormControl(false);
}

Services#

WdsDialogService, WdsDrawerService and WdsSheetService open a dialog, a drawer or a sheet from a config or with a component, which gets its data through WDS_OVERLAY_DATA. All are safe with SSR and SSG.

project-list.ts
import { Component, inject } from '@angular/core';
import { WdsDialogService } from '@wds/angular/dialog';

@Component({ /* ... */ })
export class ProjectList {
  private readonly dialogs = inject(WdsDialogService);

  remove() {
    this.dialogs
      .open<unknown, boolean>({
        title: 'Delete project?',
        description: 'This action cannot be undone.',
        okText: 'Delete',
        cancelText: 'Cancel',
        okDestructive: true,
      })
      .closed.subscribe((ok) => ok && this.delete());
  }
}
Footer