Dropdowns
Dropdowns display a list of actions or options when triggered. They help declutter interfaces by hiding secondary actions until needed.
Installation
Copy the dropdown CSS from styles/docs.css or include the Standard stylesheet:
<link rel="stylesheet" href="standard.min.css">
Then use dropdown classes in your HTML:
<div class="Dropdown">
<button class="Button Dropdown-trigger">Options <i class="ph ph-caret-down"></i></button>
<div class="Dropdown-menu">
<a href="#" class="Dropdown-item">Edit</a>
<a href="#" class="Dropdown-item">Delete</a>
</div>
</div>
Usage
Wrap a trigger and menu in .Dropdown. The trigger toggles the .Dropdown-menu visibility.
<div class="Dropdown">
<button class="Button Button--secondary Dropdown-trigger">
Options
<i class="ph ph-caret-down"></i>
</button>
<div class="Dropdown-menu" style="position: relative; display: block; margin-top: var(--space-2);">
<a href="#" class="Dropdown-item">Edit</a>
<a href="#" class="Dropdown-item">Duplicate</a>
<a href="#" class="Dropdown-item">Archive</a>
</div>
</div>
Examples
Basic Dropdown
A simple dropdown with action items and a divider.
<div class="Dropdown">
<button class="Button Button--secondary Dropdown-trigger">
Actions
<i class="ph ph-caret-down"></i>
</button>
<div class="Dropdown-menu" style="position: relative; display: block; margin-top: var(--space-2);">
<a href="#" class="Dropdown-item">Edit</a>
<a href="#" class="Dropdown-item">Duplicate</a>
<a href="#" class="Dropdown-item">Archive</a>
<hr class="Dropdown-divider">
<a href="#" class="Dropdown-item Dropdown-item--danger">Delete</a>
</div>
</div>
With Icons
Add icons to dropdown items for visual reinforcement.
<div class="Dropdown-menu" style="position: relative; display: block; width: 200px;">
<a href="#" class="Dropdown-item">
<i class="ph ph-pencil"></i>
<span>Edit</span>
</a>
<a href="#" class="Dropdown-item">
<i class="ph ph-copy"></i>
<span>Duplicate</span>
</a>
<a href="#" class="Dropdown-item">
<i class="ph ph-archive"></i>
<span>Archive</span>
</a>
<hr class="Dropdown-divider">
<a href="#" class="Dropdown-item Dropdown-item--danger">
<i class="ph ph-trash"></i>
<span>Delete</span>
</a>
</div>
With Sections
Group related items using headers and dividers.
<div class="Dropdown-menu" style="position: relative; display: block; width: 220px;">
<div class="Dropdown-header">Actions</div>
<a href="#" class="Dropdown-item">Edit profile</a>
<a href="#" class="Dropdown-item">Preferences</a>
<hr class="Dropdown-divider">
<div class="Dropdown-header">Account</div>
<a href="#" class="Dropdown-item">Billing</a>
<a href="#" class="Dropdown-item">Team members</a>
<hr class="Dropdown-divider">
<a href="#" class="Dropdown-item Dropdown-item--danger">Sign out</a>
</div>
Checkable Items
Use for multi-select or toggle options within a dropdown.
<div class="Dropdown-menu" style="position: relative; display: block; width: 180px;">
<label class="Dropdown-item Dropdown-item--check">
<input type="checkbox" checked>
<span>Show sidebar</span>
</label>
<label class="Dropdown-item Dropdown-item--check">
<input type="checkbox" checked>
<span>Show toolbar</span>
</label>
<label class="Dropdown-item Dropdown-item--check">
<input type="checkbox">
<span>Compact mode</span>
</label>
</div>
Icon Button Trigger
Common pattern for contextual action menus.
<div style="display: flex; gap: var(--space-4); align-items: flex-start;">
<div class="Dropdown">
<button class="Button Button--icon Button--secondary Dropdown-trigger" aria-label="More options">
<i class="ph ph-dots-three"></i>
</button>
</div>
<div class="Dropdown">
<button class="Button Button--icon Button--secondary Dropdown-trigger" aria-label="More options">
<i class="ph ph-dots-three-vertical"></i>
</button>
</div>
</div>
With Descriptions
Add helper text to clarify actions.
<div class="Dropdown-menu" style="position: relative; display: block; width: 260px;">
<a href="#" class="Dropdown-item Dropdown-item--descriptive">
<div class="Dropdown-item-content">
<span class="Dropdown-item-label">Public</span>
<span class="Dropdown-item-description">Anyone can view this item</span>
</div>
</a>
<a href="#" class="Dropdown-item Dropdown-item--descriptive">
<div class="Dropdown-item-content">
<span class="Dropdown-item-label">Private</span>
<span class="Dropdown-item-description">Only you can view this item</span>
</div>
</a>
<a href="#" class="Dropdown-item Dropdown-item--descriptive">
<div class="Dropdown-item-content">
<span class="Dropdown-item-label">Team only</span>
<span class="Dropdown-item-description">Only team members can view</span>
</div>
</a>
</div>
With Keyboard Shortcuts
Display keyboard shortcuts alongside actions.
<div class="Dropdown-menu" style="position: relative; display: block; width: 220px;">
<a href="#" class="Dropdown-item">
<span>Undo</span>
<kbd class="Dropdown-shortcut">⌘Z</kbd>
</a>
<a href="#" class="Dropdown-item">
<span>Redo</span>
<kbd class="Dropdown-shortcut">⌘⇧Z</kbd>
</a>
<hr class="Dropdown-divider">
<a href="#" class="Dropdown-item">
<span>Cut</span>
<kbd class="Dropdown-shortcut">⌘X</kbd>
</a>
<a href="#" class="Dropdown-item">
<span>Copy</span>
<kbd class="Dropdown-shortcut">⌘C</kbd>
</a>
<a href="#" class="Dropdown-item">
<span>Paste</span>
<kbd class="Dropdown-shortcut">⌘V</kbd>
</a>
</div>
JavaScript
Basic toggle logic for dropdown interaction:
document.querySelectorAll('.Dropdown').forEach(dropdown => {
const trigger = dropdown.querySelector('.Dropdown-trigger');
const menu = dropdown.querySelector('.Dropdown-menu');
trigger.addEventListener('click', (e) => {
e.stopPropagation();
// Close other dropdowns
document.querySelectorAll('.Dropdown-menu.is-open').forEach(m => {
if (m !== menu) m.classList.remove('is-open');
});
menu.classList.toggle('is-open');
});
});
// Close on outside click
document.addEventListener('click', () => {
document.querySelectorAll('.Dropdown-menu.is-open')
.forEach(m => m.classList.remove('is-open'));
});
// Close on Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
document.querySelectorAll('.Dropdown-menu.is-open')
.forEach(m => m.classList.remove('is-open'));
}
});
Common Patterns
User Account Menu
<div class="Dropdown-menu" style="position: relative; display: block; width: 240px;">
<div style="padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border-color);">
<div style="font-weight: 500;">John Doe</div>
<div style="font-size: 0.875rem; color: var(--fg-3);">john@example.com</div>
</div>
<a href="#" class="Dropdown-item">
<i class="ph ph-user"></i>
<span>Profile</span>
</a>
<a href="#" class="Dropdown-item">
<i class="ph ph-gear"></i>
<span>Settings</span>
</a>
<a href="#" class="Dropdown-item">
<i class="ph ph-credit-card"></i>
<span>Billing</span>
</a>
<hr class="Dropdown-divider">
<a href="#" class="Dropdown-item Dropdown-item--danger">
<i class="ph ph-sign-out"></i>
<span>Sign out</span>
</a>
</div>
Table Row Actions
<div style="display: flex; gap: var(--space-4);">
<div class="Dropdown">
<button class="Button Button--icon Button--ghost Button--small Dropdown-trigger" aria-label="Row actions">
<i class="ph ph-dots-three"></i>
</button>
</div>
<div class="Dropdown-menu" style="position: relative; display: block; width: 160px;">
<a href="#" class="Dropdown-item">
<i class="ph ph-eye"></i>
<span>View</span>
</a>
<a href="#" class="Dropdown-item">
<i class="ph ph-pencil"></i>
<span>Edit</span>
</a>
<hr class="Dropdown-divider">
<a href="#" class="Dropdown-item Dropdown-item--danger">
<i class="ph ph-trash"></i>
<span>Delete</span>
</a>
</div>
</div>
Customization
Override dropdown styles using CSS custom properties:
/* Custom dropdown menu width */
.Dropdown-menu {
--dropdown-min-width: 280px;
min-width: var(--dropdown-min-width);
}
/* Custom item colors */
.Dropdown-item {
--dropdown-item-hover: oklch(95% 0.02 250);
}
.Dropdown-item:hover {
background: var(--dropdown-item-hover);
}
/* Custom danger color */
.Dropdown-item--danger {
--dropdown-danger: oklch(55% 0.2 25);
color: var(--dropdown-danger);
}
/* Rounded menu */
.Dropdown-menu--rounded {
border-radius: var(--space-3);
}
API Reference
Container Classes
| Class | Description |
|---|---|
.Dropdown |
Container for dropdown trigger and menu |
.Dropdown-trigger |
Element that toggles the dropdown (add to button) |
.Dropdown-menu |
The dropdown menu panel |
Menu Item Classes
| Class | Description |
|---|---|
.Dropdown-item |
Individual menu item (link or button) |
.Dropdown-item--danger |
Destructive action styling (red) |
.Dropdown-item--check |
Checkable item with checkbox input |
.Dropdown-item--descriptive |
Item with description text |
Structure Classes
| Class | Description |
|---|---|
.Dropdown-header |
Section header label |
.Dropdown-divider |
Horizontal separator between items |
.Dropdown-shortcut |
Keyboard shortcut display (use with <kbd>) |
State Classes
| Class | Description |
|---|---|
.is-open |
Shows the dropdown menu (add via JS) |
CSS Reference
/* Container */
.Dropdown {
position: relative;
display: inline-block;
}
/* Menu */
.Dropdown-menu {
position: absolute;
top: 100%;
left: 0;
z-index: 100;
min-width: 180px;
padding: var(--space-1) 0;
background: var(--bg);
border: 1px solid var(--bd);
border-radius: var(--r-m);
box-shadow: 0 4px 12px oklch(0% 0 0 / 0.12);
display: none;
margin-top: var(--space-1);
}
.Dropdown-menu.is-open {
display: block;
}
/* Items */
.Dropdown-item {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-2) var(--space-3);
color: var(--fg);
text-decoration: none;
font-size: 0.9rem;
cursor: pointer;
transition: background 0.1s;
}
.Dropdown-item:hover {
background: var(--bg-s);
}
.Dropdown-item--danger {
color: oklch(55% 0.2 25);
}
.Dropdown-item--danger:hover {
background: oklch(55% 0.2 25 / 0.08);
}
/* Checkable items */
.Dropdown-item--check {
cursor: pointer;
}
.Dropdown-item--check input {
margin: 0;
}
/* Descriptive items */
.Dropdown-item--descriptive {
padding: var(--space-2) var(--space-3);
}
.Dropdown-item-content {
display: flex;
flex-direction: column;
}
.Dropdown-item-label {
font-weight: 500;
}
.Dropdown-item-description {
font-size: 0.8rem;
color: var(--fg-3);
}
/* Divider */
.Dropdown-divider {
border: none;
border-top: 1px solid var(--bd);
margin: var(--space-1) 0;
}
/* Header */
.Dropdown-header {
padding: var(--space-2) var(--space-3);
font-size: 0.75rem;
font-weight: 600;
color: var(--fg-3);
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* Keyboard shortcut */
.Dropdown-shortcut {
margin-left: auto;
font-size: 0.75rem;
color: var(--fg-3);
font-family: var(--font-sans);
}
/* Trigger caret */
.Dropdown-trigger i.ph-caret-down {
font-size: 0.8rem;
transition: transform 0.15s;
}
.Dropdown-trigger[aria-expanded="true"] i.ph-caret-down {
transform: rotate(180deg);
}
Accessibility
Keyboard Support
| Key | Action |
|---|---|
| Enter / Space | Opens dropdown, activates focused item |
| Escape | Closes dropdown |
| Arrow Down | Moves focus to next item |
| Arrow Up | Moves focus to previous item |
| Home | Moves focus to first item |
| End | Moves focus to last item |
| Tab | Closes dropdown, moves to next element |
ARIA Attributes
<!-- Proper dropdown markup -->
<div class="Dropdown">
<button
class="Button Dropdown-trigger"
aria-haspopup="true"
aria-expanded="false"
aria-controls="dropdown-menu-1"
>
Options
<i class="ph ph-caret-down" aria-hidden="true"></i>
</button>
<div
class="Dropdown-menu"
id="dropdown-menu-1"
role="menu"
aria-labelledby="dropdown-trigger-1"
>
<a href="#" class="Dropdown-item" role="menuitem">Edit</a>
<a href="#" class="Dropdown-item" role="menuitem">Duplicate</a>
<hr class="Dropdown-divider" role="separator">
<a href="#" class="Dropdown-item Dropdown-item--danger" role="menuitem">Delete</a>
</div>
</div>
<!-- When open, update aria-expanded -->
<button aria-expanded="true">...</button>
<!-- Checkable items use menuitemcheckbox -->
<label class="Dropdown-item Dropdown-item--check" role="menuitemcheckbox" aria-checked="true">
<input type="checkbox" checked>
<span>Show sidebar</span>
</label>
Focus Management
// Move focus into menu when opened
menu.classList.add('is-open');
menu.querySelector('.Dropdown-item')?.focus();
// Return focus to trigger when closed
menu.classList.remove('is-open');
trigger.focus();
Best Practices
Do
- ✓ Group related actions — Use headers and dividers to organize
- ✓ Put destructive actions last — Separate with a divider
- ✓ Use clear action labels — “Delete project” not just “Delete”
- ✓ Add keyboard shortcuts — For frequently used actions
- ✓ Keep menus focused — 5-7 items max before grouping
- ✓ Close on action — Dismiss dropdown after user selects
Don’t
- ✗ Nest dropdowns — Use a different pattern for sub-menus
- ✗ Use for navigation — Dropdowns are for actions, not nav links
- ✗ Hide primary actions — Important actions should be visible
- ✗ Overload with options — Too many items overwhelm users
- ✗ Forget mobile — Ensure touch targets are large enough (44px min)
- ✗ Skip the caret icon — Visual affordance helps discoverability