General
Kbd
Usage#
import { WDS_KBD } from '@wds/angular/kbd';Examples#
Default#
import { Component } from '@angular/core';
import { WdsKbdComponent, WdsKbdGroupComponent } from '@wds/angular/kbd';
@Component({
selector: 'app-kbd-default-demo',
imports: [WdsKbdGroupComponent, WdsKbdComponent],
template: `
<div class="flex flex-col items-center gap-4">
<wds-kbd-group>
<wds-kbd>⌘</wds-kbd>
<wds-kbd>⇧</wds-kbd>
<wds-kbd>⌥</wds-kbd>
<wds-kbd>⌃</wds-kbd>
</wds-kbd-group>
<wds-kbd-group>
<wds-kbd>Ctrl</wds-kbd>
<span>+</span>
<wds-kbd>B</wds-kbd>
</wds-kbd-group>
</div>
`,
})
export class KbdDefaultDemo {}Group#
wds-kbd-group keeps keys and text on one line with an even gap.
import { Component } from '@angular/core';
import { WdsKbdComponent, WdsKbdGroupComponent } from '@wds/angular/kbd';
@Component({
selector: 'app-kbd-group-demo',
imports: [WdsKbdGroupComponent, WdsKbdComponent],
template: `
<wds-kbd-group class="text-sm text-muted-foreground">
Use
<wds-kbd>Ctrl + B</wds-kbd>
<wds-kbd>Ctrl + K</wds-kbd>
to open the command palette
</wds-kbd-group>
`,
})
export class KbdGroupDemo {}Button#
<svg> in wds-kbd gets 12px.
import { Component } from '@angular/core';
import { WdsButtonComponent } from '@wds/angular/button';
import { WdsKbdComponent } from '@wds/angular/kbd';
@Component({
selector: 'app-kbd-button-demo',
imports: [WdsButtonComponent, WdsKbdComponent],
template: `
<wds-button variant="outline">
Accept
<wds-kbd>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 4v7a4 4 0 0 1-4 4H4" /><path d="m9 10-5 5 5 5" /></svg>
</wds-kbd>
</wds-button>
`,
})
export class KbdButtonDemo {}Input group#
import { Component } from '@angular/core';
import { WdsInputGroupAddonComponent, WdsInputGroupComponent } from '@wds/angular/input-group';
import { WdsInputComponent } from '@wds/angular/input';
import { WdsKbdComponent, WdsKbdGroupComponent } from '@wds/angular/kbd';
@Component({
selector: 'app-kbd-input-group-demo',
imports: [WdsInputGroupComponent, WdsInputGroupAddonComponent, WdsInputComponent, WdsKbdGroupComponent, WdsKbdComponent],
template: `
<wds-input-group class="w-full max-w-xs">
<wds-input-group-addon>
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m21 21-4.34-4.34" /><circle cx="11" cy="11" r="8" /></svg>
</wds-input-group-addon>
<wds-input type="search" placeholder="Search…" aria-label="Search"></wds-input>
<wds-input-group-addon align="inline-end">
<wds-kbd-group><wds-kbd>⌘</wds-kbd><wds-kbd>K</wds-kbd></wds-kbd-group>
</wds-input-group-addon>
</wds-input-group>
`,
})
export class KbdInputGroupDemo {}Shortcut#
wds-kbd only displays a key: the app handles the shortcut, usually with one keydown listener on document, so a key described in several places acts once. Check metaKey || ctrlKey and show ⌘ on macOS and Ctrl elsewhere (detect the platform in the browser, not during prerendering). Call preventDefault() when the browser has its own action (⌘S saves the page), skip e.repeat, and avoid shortcuts reserved by the browser or the system (⌘T, ⌘W, ⌘N, ⌘Q). Ignore shortcuts without a modifier (such as /) while the user types in a field.
Click the button or press the shortcut.
import { Component, afterNextRender, signal } from '@angular/core';
import { WdsButtonComponent } from '@wds/angular/button';
import { WDS_KBD } from '@wds/angular/kbd';
@Component({
selector: 'app-kbd-shortcut-demo',
imports: [...WDS_KBD, WdsButtonComponent],
// One listener on the document: the shortcut works wherever the focus is.
host: { '(document:keydown)': 'onKeydown($event)' },
template: `
<div class="flex flex-col items-center gap-3">
<wds-button variant="outline" [attr.aria-keyshortcuts]="isMac() ? 'Meta+S' : 'Control+S'" (click)="save()">
Save
<wds-kbd-group>
<wds-kbd>{{ isMac() ? '⌘' : 'Ctrl' }}</wds-kbd>
<wds-kbd>S</wds-kbd>
</wds-kbd-group>
</wds-button>
<p class="m-0 text-sm text-muted-foreground" aria-live="polite">
{{ count() ? 'Saved ' + count() + '×' : 'Click the button or press the shortcut.' }}
</p>
</div>
`,
})
export class KbdShortcutDemo {
// The platform is known only in the browser (not during prerendering).
protected readonly isMac = signal(true);
protected readonly count = signal(0);
constructor() {
afterNextRender(() => this.isMac.set(/Mac|iPhone|iPad/.test(navigator.userAgent)));
}
protected onKeydown(e: KeyboardEvent): void {
// ⌘ on macOS, Ctrl elsewhere; without Alt/Shift so other shortcuts still work.
if (e.key?.toLowerCase() !== 's' || !(e.metaKey || e.ctrlKey) || e.altKey || e.shiftKey) return;
e.preventDefault(); // otherwise the browser opens "Save page"
if (!e.repeat) this.save(); // ignore a held key
}
protected save(): void {
this.count.update((n) => n + 1);
}
}API References#
wds-kbd#
<kbd>), e.g. ⌘, Ctrl or Ctrl + K. It only displays the key:
it does not listen to the keyboard. wds-kbd-group groups several keys.
Slots
| Slot | Description |
|---|---|
| (default) |
CSS parts (::part)
| Part | Description |
|---|---|
| kbd | <kbd> element. |
CSS custom properties
| Property | Description |
|---|---|
| --wds-kbd-bg | --wds-muted). Change it e.g. on the dark background of a tooltip. |
| --wds-kbd-fg | --wds-muted-foreground). |
wds-kbd-group#
Ctrl + B). Plain text can go between the wds-kbd elements.
Slots
| Slot | Description |
|---|---|
| (default) | wds-kbd elements and text. |
Accessibility#
Screen reader#
wds-kbd renders a native <kbd>; screen readers read its text. Symbols such as ⌘, ⇧ or ⌥ are read differently, or not at all, depending on the screen reader, so do not rely on them alone to explain an action.
aria-keyshortcuts (e.g. Meta+S); wds-button forwards it to the native button.
wds-kbd-group is only a layout container without a role.
Keyboard support#
wds-kbd has no keyboard interaction and ignores the pointer. The shortcuts it describes are handled by the app.