Navigation
Dropdown
Usage#
import { WDS_DROPDOWN } from '@wds/angular/dropdown';Examples#
Default#
trigger slot opens the menu. Selecting an item fires wds-select with its value and closes the menu.
import { Component } from '@angular/core';
import { WdsDropdownComponent, WdsDropdownItemComponent, WdsDropdownLabelComponent, WdsDropdownSeparatorComponent } from '@wds/angular/dropdown';
import { WdsButtonComponent } from '@wds/angular/button';
@Component({
selector: 'app-dropdown-default-demo',
imports: [WdsDropdownComponent, WdsDropdownLabelComponent, WdsDropdownItemComponent, WdsDropdownSeparatorComponent, WdsButtonComponent],
template: `
<wds-dropdown>
<wds-button slot="trigger" variant="outline">Open</wds-button>
<wds-dropdown-label>My Account</wds-dropdown-label>
<wds-dropdown-item value="profile">Profile</wds-dropdown-item>
<wds-dropdown-item value="billing">Billing</wds-dropdown-item>
<wds-dropdown-item value="settings">Settings</wds-dropdown-item>
<wds-dropdown-separator></wds-dropdown-separator>
<wds-dropdown-item value="logout">Log out</wds-dropdown-item>
</wds-dropdown>
`,
})
export class DropdownDefaultDemo {}Shortcuts#
shortcut slot shows a keyboard shortcut on the right. It is only displayed: handle the keys yourself.
import { Component } from '@angular/core';
import { WdsDropdownComponent, WdsDropdownItemComponent, WdsDropdownLabelComponent, WdsDropdownSeparatorComponent } from '@wds/angular/dropdown';
import { WdsButtonComponent } from '@wds/angular/button';
@Component({
selector: 'app-dropdown-shortcuts-demo',
imports: [WdsDropdownComponent, WdsDropdownLabelComponent, WdsDropdownItemComponent, WdsDropdownSeparatorComponent, WdsButtonComponent],
template: `
<wds-dropdown>
<wds-button slot="trigger" variant="outline">Open</wds-button>
<wds-dropdown-label>My Account</wds-dropdown-label>
<wds-dropdown-item value="profile">Profile<span slot="shortcut">⇧⌘P</span></wds-dropdown-item>
<wds-dropdown-item value="billing">Billing<span slot="shortcut">⌘B</span></wds-dropdown-item>
<wds-dropdown-item value="settings">Settings<span slot="shortcut">⌘S</span></wds-dropdown-item>
<wds-dropdown-separator></wds-dropdown-separator>
<wds-dropdown-item value="logout">Log out<span slot="shortcut">⇧⌘Q</span></wds-dropdown-item>
</wds-dropdown>
`,
})
export class DropdownShortcutsDemo {}Icons#
<svg> before the text gets the icon size and spacing.
import { Component } from '@angular/core';
import { WdsDropdownComponent, WdsDropdownItemComponent, WdsDropdownSeparatorComponent } from '@wds/angular/dropdown';
import { WdsButtonComponent } from '@wds/angular/button';
@Component({
selector: 'app-dropdown-icons-demo',
imports: [WdsDropdownComponent, WdsDropdownItemComponent, WdsDropdownSeparatorComponent, WdsButtonComponent],
template: `
<wds-dropdown>
<wds-button slot="trigger" variant="outline">Open</wds-button>
<wds-dropdown-item value="profile">
<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="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" /></svg>
Profile
</wds-dropdown-item>
<wds-dropdown-item value="billing">
<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"><rect width="20" height="14" x="2" y="5" rx="2" /><path d="M2 10h20" /></svg>
Billing
</wds-dropdown-item>
<wds-dropdown-item value="settings">
<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="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" /><circle cx="12" cy="12" r="3" /></svg>
Settings
</wds-dropdown-item>
<wds-dropdown-separator></wds-dropdown-separator>
<wds-dropdown-item value="logout">
<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="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" /><path d="m16 17 5-5-5-5" /><path d="M21 12H9" /></svg>
Log out
</wds-dropdown-item>
</wds-dropdown>
`,
})
export class DropdownIconsDemo {}Checkboxes#
type="checkbox" toggles checked on selection; detail.checked in wds-select is the new state. The menu closes after each change.
Status Bar: on, Panel: off
import { Component, signal } from '@angular/core';
import { WdsButtonComponent } from '@wds/angular/button';
import { WDS_DROPDOWN } from '@wds/angular/dropdown';
@Component({
selector: 'app-dropdown-checkboxes-demo',
imports: [...WDS_DROPDOWN, WdsButtonComponent],
template: `
<div class="flex flex-col items-center gap-3">
<wds-dropdown>
<wds-button slot="trigger" variant="outline">View</wds-button>
<wds-dropdown-label>Appearance</wds-dropdown-label>
<wds-dropdown-separator />
<wds-dropdown-item type="checkbox" [checked]="statusBar()" (wdsSelect)="statusBar.set(!!$event.checked)">Status Bar</wds-dropdown-item>
<wds-dropdown-item type="checkbox" [checked]="activityBar()" disabled>Activity Bar</wds-dropdown-item>
<wds-dropdown-item type="checkbox" [checked]="panel()" (wdsSelect)="panel.set(!!$event.checked)">Panel</wds-dropdown-item>
</wds-dropdown>
<p class="m-0 text-sm text-muted-foreground">Status Bar: {{ statusBar() ? 'on' : 'off' }}, Panel: {{ panel() ? 'on' : 'off' }}</p>
</div>
`,
})
export class DropdownCheckboxesDemo {
protected readonly statusBar = signal(true);
protected readonly activityBar = signal(false);
protected readonly panel = signal(false);
}Radio group#
wds-dropdown-radio-group keeps one checked type="radio" item in value and fires wds-change.
Position: bottom
import { Component, signal } from '@angular/core';
import { WdsButtonComponent } from '@wds/angular/button';
import { WDS_DROPDOWN } from '@wds/angular/dropdown';
@Component({
selector: 'app-dropdown-radio-group-demo',
imports: [...WDS_DROPDOWN, WdsButtonComponent],
template: `
<div class="flex flex-col items-center gap-3">
<wds-dropdown>
<wds-button slot="trigger" variant="outline">Panel Position</wds-button>
<wds-dropdown-label>Panel Position</wds-dropdown-label>
<wds-dropdown-separator />
<wds-dropdown-radio-group [value]="position()" (wdsChange)="position.set($event.value)">
<wds-dropdown-item type="radio" value="top">Top</wds-dropdown-item>
<wds-dropdown-item type="radio" value="bottom">Bottom</wds-dropdown-item>
<wds-dropdown-item type="radio" value="right">Right</wds-dropdown-item>
</wds-dropdown-radio-group>
</wds-dropdown>
<p class="m-0 text-sm text-muted-foreground">Position: {{ position() }}</p>
</div>
`,
})
export class DropdownRadioGroupDemo {
protected readonly position = signal('bottom');
}Destructive#
variant="destructive" marks an item with a dangerous action.
import { Component } from '@angular/core';
import { WdsDropdownComponent, WdsDropdownItemComponent, WdsDropdownSeparatorComponent } from '@wds/angular/dropdown';
import { WdsButtonComponent } from '@wds/angular/button';
@Component({
selector: 'app-dropdown-destructive-demo',
imports: [WdsDropdownComponent, WdsDropdownItemComponent, WdsDropdownSeparatorComponent, WdsButtonComponent],
template: `
<wds-dropdown>
<wds-button slot="trigger" variant="outline">Actions</wds-button>
<wds-dropdown-item value="edit">Edit</wds-dropdown-item>
<wds-dropdown-item value="share">Share</wds-dropdown-item>
<wds-dropdown-separator></wds-dropdown-separator>
<wds-dropdown-item value="delete" variant="destructive">
<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="M3 6h18" /><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" /><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" /></svg>
Delete
</wds-dropdown-item>
</wds-dropdown>
`,
})
export class DropdownDestructiveDemo {}Submenu#
wds-dropdown with a wds-dropdown-item in its trigger slot is a submenu. It opens on hover, click or ArrowRight.
import { Component } from '@angular/core';
import { WdsDropdownComponent, WdsDropdownItemComponent, WdsDropdownSeparatorComponent } from '@wds/angular/dropdown';
import { WdsButtonComponent } from '@wds/angular/button';
@Component({
selector: 'app-dropdown-submenu-demo',
imports: [WdsDropdownComponent, WdsDropdownItemComponent, WdsDropdownSeparatorComponent, WdsButtonComponent],
template: `
<wds-dropdown>
<wds-button slot="trigger" variant="outline">Open</wds-button>
<wds-dropdown-item value="team">Team</wds-dropdown-item>
<wds-dropdown>
<wds-dropdown-item slot="trigger">Invite users</wds-dropdown-item>
<wds-dropdown-item value="invite-email">Email</wds-dropdown-item>
<wds-dropdown-item value="invite-message">Message</wds-dropdown-item>
<wds-dropdown-separator></wds-dropdown-separator>
<wds-dropdown-item value="invite-more">More…</wds-dropdown-item>
</wds-dropdown>
<wds-dropdown-item value="new-team">New Team<span slot="shortcut">⌘T</span></wds-dropdown-item>
</wds-dropdown>
`,
})
export class DropdownSubmenuDemo {}Complex#
import { Component } from '@angular/core';
import { WdsDropdownComponent, WdsDropdownGroupComponent, WdsDropdownItemComponent, WdsDropdownLabelComponent, WdsDropdownSeparatorComponent } from '@wds/angular/dropdown';
import { WdsButtonComponent } from '@wds/angular/button';
@Component({
selector: 'app-dropdown-complex-demo',
imports: [WdsDropdownComponent, WdsDropdownLabelComponent, WdsDropdownGroupComponent, WdsDropdownItemComponent, WdsDropdownSeparatorComponent, WdsButtonComponent],
template: `
<wds-dropdown>
<wds-button slot="trigger" variant="outline">Account</wds-button>
<wds-dropdown-label>My Account</wds-dropdown-label>
<wds-dropdown-group>
<wds-dropdown-item value="profile">Profile<span slot="shortcut">⇧⌘P</span></wds-dropdown-item>
<wds-dropdown-item value="billing">Billing<span slot="shortcut">⌘B</span></wds-dropdown-item>
<wds-dropdown-item value="settings">Settings<span slot="shortcut">⌘S</span></wds-dropdown-item>
<wds-dropdown-item value="shortcuts">Keyboard shortcuts<span slot="shortcut">⌘K</span></wds-dropdown-item>
</wds-dropdown-group>
<wds-dropdown-separator></wds-dropdown-separator>
<wds-dropdown-group>
<wds-dropdown-item value="team">Team</wds-dropdown-item>
<wds-dropdown>
<wds-dropdown-item slot="trigger">Invite users</wds-dropdown-item>
<wds-dropdown-item value="invite-email">Email</wds-dropdown-item>
<wds-dropdown-item value="invite-message">Message</wds-dropdown-item>
</wds-dropdown>
<wds-dropdown-item value="new-team">New Team<span slot="shortcut">⌘T</span></wds-dropdown-item>
</wds-dropdown-group>
<wds-dropdown-separator></wds-dropdown-separator>
<wds-dropdown-item value="github">GitHub</wds-dropdown-item>
<wds-dropdown-item value="support">Support</wds-dropdown-item>
<wds-dropdown-item value="api" disabled>API</wds-dropdown-item>
<wds-dropdown-separator></wds-dropdown-separator>
<wds-dropdown-item value="logout">Log out<span slot="shortcut">⇧⌘Q</span></wds-dropdown-item>
</wds-dropdown>
`,
})
export class DropdownComplexDemo {}Placement#
side and align place the panel around the trigger. It flips to the other side when there is not enough space.
import { Component } from '@angular/core';
import { WdsDropdownComponent, WdsDropdownItemComponent } from '@wds/angular/dropdown';
import { WdsButtonComponent } from '@wds/angular/button';
@Component({
selector: 'app-dropdown-placement-demo',
imports: [WdsDropdownComponent, WdsDropdownItemComponent, WdsButtonComponent],
template: `
<div class="flex flex-wrap items-center justify-center gap-2">
<wds-dropdown side="top">
<wds-button slot="trigger" variant="outline">Top</wds-button>
<wds-dropdown-item value="profile">Profile</wds-dropdown-item>
<wds-dropdown-item value="billing">Billing</wds-dropdown-item>
</wds-dropdown>
<wds-dropdown side="right">
<wds-button slot="trigger" variant="outline">Right</wds-button>
<wds-dropdown-item value="profile">Profile</wds-dropdown-item>
<wds-dropdown-item value="billing">Billing</wds-dropdown-item>
</wds-dropdown>
<wds-dropdown align="end">
<wds-button slot="trigger" variant="outline">Bottom end</wds-button>
<wds-dropdown-item value="profile">Profile</wds-dropdown-item>
<wds-dropdown-item value="billing">Billing</wds-dropdown-item>
</wds-dropdown>
</div>
`,
})
export class DropdownPlacementDemo {}Hover#
trigger="hover" also opens the menu when the pointer is over the trigger; a click and the keyboard still work.
import { Component } from '@angular/core';
import { WdsDropdownComponent, WdsDropdownItemComponent } from '@wds/angular/dropdown';
import { WdsButtonComponent } from '@wds/angular/button';
@Component({
selector: 'app-dropdown-hover-demo',
imports: [WdsDropdownComponent, WdsDropdownItemComponent, WdsButtonComponent],
template: `
<wds-dropdown trigger="hover">
<wds-button slot="trigger" variant="outline">Hover</wds-button>
<wds-dropdown-item value="profile">Profile</wds-dropdown-item>
<wds-dropdown-item value="billing">Billing</wds-dropdown-item>
<wds-dropdown-item value="settings">Settings</wds-dropdown-item>
</wds-dropdown>
`,
})
export class DropdownHoverDemo {}API References#
wds-dropdown#
wds-dropdown with a wds-dropdown-item in the trigger slot is a submenu.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| side | WdsDropdownSide | undefined | bottom, in a submenu right. Flips when there is not enough space. | |
| align | WdsDropdownAlign | 'start' | |
| offset | number | undefined | ||
| trigger | WdsDropdownTrigger | 'click' | hover opens the menu when the pointer is over the trigger (a click still works). |
| open | boolean | false | show(), close() and toggle(). |
| disabled | boolean | false |
Events
| Event | Description |
|---|---|
| wds-open-changeCustomEvent<WdsDropdownOpenChangeDetail> | |
| wds-selectCustomEvent<WdsDropdownSelectDetail> | wds-dropdown-item). |
Methods
| Method | Description |
|---|---|
| show() | |
| close() | |
| toggle() |
Slots
| Slot | Description |
|---|---|
| trigger | wds-button). In a submenu: a wds-dropdown-item. |
| (default) | wds-dropdown-item, -label, -separator, -group, -radio-group, submenus. |
CSS parts (::part)
| Part | Description |
|---|---|
| content |
CSS custom properties
| Property | Description |
|---|---|
| --wds-dropdown-min-width |
wds-dropdown-item#
trigger slot of a nested wds-dropdown it opens the submenu.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| type | WdsDropdownItemType | 'item' | checkbox toggles checked; radio works in a wds-dropdown-radio-group. |
| value | string | '' | wds-select (and the value in a radio group). |
| checked | boolean | false | checkbox or radio item. |
| disabled | boolean | false | |
| variant | WdsDropdownItemVariant | 'default' | destructive: the error color (e.g. "Delete"). |
| inset | boolean | false |
Events
| Event | Description |
|---|---|
| wds-selectCustomEvent<WdsDropdownSelectDetail> |
Slots
| Slot | Description |
|---|---|
| (default) | <svg> icon). |
| shortcut | ⌘K. It is only displayed; handle the keys yourself. |
CSS parts (::part)
| Part | Description |
|---|---|
| base |
wds-dropdown-label#
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| inset | boolean | false |
Slots
| Slot | Description |
|---|---|
| (default) |
CSS parts (::part)
| Part | Description |
|---|---|
| base |
wds-dropdown-separator#
role="separator").
CSS parts (::part)
| Part | Description |
|---|---|
| base |
wds-dropdown-group#
role="group").
Slots
| Slot | Description |
|---|---|
| (default) | wds-dropdown-item items. |
wds-dropdown-radio-group#
type="radio" items with one selected value.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| value | string | '' |
Events
| Event | Description |
|---|---|
| wds-changeCustomEvent<WdsDropdownChangeDetail> |
Slots
| Slot | Description |
|---|---|
| (default) | wds-dropdown-item type="radio" items. |
Accessibility#
Screen reader#
aria-haspopup="menu" and aria-expanded. The panel has role="menu"; give the trigger a text or a label that says what the menu is for.
role="menuitem", or menuitemcheckbox / menuitemradio with aria-checked. Disabled items have aria-disabled="true". Groups have role="group" and separators role="separator".
wds-dropdown-label is plain text and is not linked to a group; to name a group, set aria-label on wds-dropdown-group. Give icons aria-hidden="true".
shortcut slot is part of the item name, so screen readers read it after the label.
Keyboard support#
| Key | Function |
|---|---|