LiveAnnouncer

Signal-based wrapper around Angular CDK's LiveAnnouncer. Reactively track and announce messages for screen readers.

CDK Interop Package Required

This utility requires the @signality/cdk-interop and @angular/cdk packages to be installed:

bash
npm install @signality/cdk-interop @angular/cdk

Usage

angular-ts
import { Component } from '@angular/core';
import { liveAnnouncer } from '@signality/cdk-interop';

@Component({
  template: `
    <button (click)="save()">Save</button>
    <p>Last announcement: {{ announcer.lastMessage() ?? 'none' }}</p>
  `,
})
export class SaveButton {
  readonly announcer = liveAnnouncer(); 
  
  save() {
    this.announcer.announce('Document saved successfully');
  }
}

Parameters

ParameterTypeDescription
optionsLiveAnnouncerOptionsOptional configuration (see Options below)

Options

The LiveAnnouncerOptions extends WithInjector:

OptionTypeDefaultDescription
defaultPoliteness'polite' | 'assertive' | 'off''polite'Default politeness level for announcements. See ARIA live regions
injectorInjector-Optional injector for DI context

Return Value

The liveAnnouncer() function returns a LiveAnnouncerRef object:

PropertyTypeDescription
lastMessageSignal<string | null>Last announced message
announce(message: string, politeness?: AriaLivePoliteness) => voidAnnounce a message to screen readers
clear() => voidClear all announcements

Examples

Form validation feedback

angular-ts
import { Component, effect } from '@angular/core';
import { liveAnnouncer } from '@signality/cdk-interop';

@Component({
  template: `
    <form>
      <input #email type="email" />
      <button type="submit">Submit</button>
    </form>
  `,
})
export class ContactForm {
  readonly announcer = liveAnnouncer();
  readonly email = viewChild<ElementRef>('email');
  
  onSubmit() {
    if (!this.email()?.nativeElement.validity.valid) {
      this.announcer.announce('Please enter a valid email', 'assertive'); 
    }
  }
}

Dynamic content updates

angular-ts
import { Component, effect } from '@angular/core';
import { liveAnnouncer } from '@signality/cdk-interop';

@Component({
  template: `
    <button (click)="addItem()">Add Item</button>
    <p>Items: {{ items().length }}</p>
  `,
})
export class ShoppingCart {
  readonly announcer = liveAnnouncer();
  readonly items = signal<string[]>([]);
  
  addItem() {
    this.items.update(items => [...items, 'New item']);
    this.announcer.announce(`Item added. Total: ${this.items().length}`); 
  }
}
angular-ts
import { Component, inject, DOCUMENT } from '@angular/core';
import { liveAnnouncer } from '@signality/cdk-interop';
import { routerListener } from '@signality/core';

@Component({
  template: `<router-outlet />`,
})
export class App {
  readonly document = inject(DOCUMENT);
  readonly announcer = liveAnnouncer();
  
  constructor() {
    routerListener('navigationend', () => {
      const pageTitle = this.document.title || 'Page loaded';
      this.announcer.announce(`Navigated to ${pageTitle}`); 
    });
  }
}

Type Definitions

typescript
type AriaLivePoliteness = 'polite' | 'assertive' | 'off';

interface LiveAnnouncerOptions extends WithInjector {
  readonly defaultPoliteness?: AriaLivePoliteness;
}

interface LiveAnnouncerRef {
  readonly lastMessage: Signal<string | null>;
  readonly announce: (message: string, politeness?: AriaLivePoliteness) => void;
  readonly clear: () => void;
}

function liveAnnouncer(options?: LiveAnnouncerOptions): LiveAnnouncerRef;
Edit this page on GitHub Last updated: Mar 19, 2026, 23:28:23