Appearance
@vibe-labs/design-components-dropdowns
Dropdown menu component tokens, styles, and TypeScript types for the Vibe Design System.
Installation
bash
npm install @vibe-labs/design-components-dropdownscss
@import "@vibe-labs/design-components-dropdowns";ts
import { DropdownAlignments, DropdownItemVariants } from "@vibe-labs/design-components-dropdowns/types";
import type { DropdownMenuStyleProps, DropdownItemStyleProps } from "@vibe-labs/design-components-dropdowns/types";Contents
Tokens
CSS custom properties defined in @layer vibe.tokens (file: dropdown.css).
Menu
| Token | Default | Description |
|---|---|---|
--dropdown-bg | --surface-elevated | Menu background |
--dropdown-border-color | --border-subtle | Menu border |
--dropdown-border-width | --border-width-1 | Border width |
--dropdown-radius | --radius-lg | Border radius |
--dropdown-shadow | --shadow-lg | Box shadow |
--dropdown-padding | --space-1 | Inner padding |
--dropdown-min-width | 10rem | Minimum width |
--dropdown-max-height | 20rem | Max height (scrollable) |
--dropdown-z | --z-dropdown (100) | Z-index |
Items
| Token | Default | Description |
|---|---|---|
--dropdown-item-height | 2rem | Minimum item height |
--dropdown-item-px | --space-2 | Horizontal padding |
--dropdown-item-radius | --radius-sm | Item border radius |
--dropdown-item-font-size | --text-sm | Font size |
--dropdown-item-color | --text-primary | Text color |
--dropdown-item-hover-bg | --surface-hover-overlay | Hover background |
--dropdown-item-active-bg | --surface-active-overlay | Active background |
--dropdown-item-disabled-opacity | --input-disabled-opacity | Disabled state opacity |
Groups
| Token | Default | Description |
|---|---|---|
--dropdown-group-label-font-size | --text-xs | Group label size |
--dropdown-group-label-color | --text-muted | Group label color |
--dropdown-divider-color | --border-subtle | Divider color |
Generated Styles
Component classes generated into @layer vibe.components (file: dropdown.g.css).
Elements
| Class | Description |
|---|---|
.dropdown | Relative positioned inline-flex wrapper |
.dropdown-menu | Absolutely positioned panel (hidden by default, flex column when open) |
.dropdown-item | Full-width interactive row with hover/active/focus/disabled states |
.dropdown-item-icon | Leading icon slot (1rem, muted) |
.dropdown-item-trail | Trailing content (auto margin-left, muted, xs text) |
.dropdown-item-desc | Description text below item (xs, muted, wraps) |
.dropdown-group | Flex column container for grouped items |
.dropdown-group-label | Uppercase section label |
.dropdown-divider | Horizontal rule between sections |
Menu Alignment
| Value | Position |
|---|---|
bottom-start | Below, left-aligned |
bottom-end | Below, right-aligned |
bottom-center | Below, centered |
top-start | Above, left-aligned |
top-end | Above, right-aligned |
Set via data-align on .dropdown-menu.
Item Variants
data-variant="danger" — red text with danger-subtle hover background.
Selection States
Items with aria-selected="true" or aria-checked="true" receive hover overlay background and semibold weight.
Modifiers
| Modifier | Description |
|---|---|
data-open | Shows menu with fade-in animation |
data-full | Menu matches trigger width |
TypeScript Types
ts
DropdownAlignments // ["bottom-start", "bottom-end", "bottom-center", "top-start", "top-end"]
DropdownItemVariants // ["danger"]
type DropdownAlignment
type DropdownItemVariant
interface DropdownStyleProps {}
interface DropdownMenuStyleProps { align?: DropdownAlignment; open?: boolean; full?: boolean }
interface DropdownItemStyleProps { variant?: DropdownItemVariant; disabled?: boolean }
interface DropdownItemIconStyleProps {}
interface DropdownItemTrailStyleProps {}
interface DropdownItemDescStyleProps {}
interface DropdownGroupStyleProps {}
interface DropdownGroupLabelStyleProps {}
interface DropdownDividerStyleProps {}Dist Structure
| File | Description |
|---|---|
index.css | Barrel — imports tokens + generated styles |
dropdown.css | Token definitions |
dropdown.g.css | Generated component styles |
index.js / index.d.ts | TypeScript types + runtime constants |
Dependencies
Requires tokens from @vibe-labs/design (surfaces, borders, spacing, typography, elevation, transitions, z-index).
Build
bash
npm run buildUsage Guide
Import
css
@import "@vibe-labs/design-components-dropdowns";ts
import type { DropdownMenuStyleProps, DropdownItemStyleProps } from "@vibe-labs/design-components-dropdowns/types";Variants
Menu alignment
Set data-align on .dropdown-menu: bottom-start (default) · bottom-end · bottom-center · top-start · top-end
Item variant
Set data-variant on .dropdown-item: omit for default · danger
Boolean flags on .dropdown-menu
data-open— shows the menu with fade-in animationdata-full— menu width matches the trigger button width
Item states
data-disabledon.dropdown-item— reduces opacity, disables pointer eventsaria-selected="true"oraria-checked="true"on.dropdown-item— shows selected state (semibold, overlay bg)
Examples
Basic dropdown
html
<!-- Toggle data-open to show/hide the menu -->
<div class="dropdown">
<button class="btn" data-variant="secondary" aria-haspopup="true" aria-expanded="true">
Options
</button>
<div class="dropdown-menu" data-align="bottom-start" data-open>
<button class="dropdown-item">Edit</button>
<button class="dropdown-item">Duplicate</button>
<div class="dropdown-divider"></div>
<button class="dropdown-item" data-variant="danger">Delete</button>
</div>
</div>Items with icons and trailing text
html
<div class="dropdown-menu" data-align="bottom-start" data-open>
<button class="dropdown-item">
<span class="dropdown-item-icon">
<svg><!-- icon --></svg>
</span>
Copy link
<span class="dropdown-item-trail">⌘C</span>
</button>
<button class="dropdown-item">
<span class="dropdown-item-icon">
<svg><!-- icon --></svg>
</span>
Share
</button>
</div>Items with descriptions
html
<div class="dropdown-menu" data-align="bottom-start" data-open>
<button class="dropdown-item">
Public
<span class="dropdown-item-desc">Anyone with the link can view.</span>
</button>
<button class="dropdown-item" aria-selected="true">
Team only
<span class="dropdown-item-desc">Only team members can view.</span>
</button>
<button class="dropdown-item">
Private
<span class="dropdown-item-desc">Only you can view.</span>
</button>
</div>Grouped items
html
<div class="dropdown-menu" data-align="bottom-start" data-open>
<div class="dropdown-group">
<div class="dropdown-group-label">Account</div>
<button class="dropdown-item">Profile</button>
<button class="dropdown-item">Settings</button>
</div>
<div class="dropdown-divider"></div>
<div class="dropdown-group">
<div class="dropdown-group-label">Danger zone</div>
<button class="dropdown-item" data-variant="danger">Sign out</button>
</div>
</div>Full-width dropdown (matches trigger)
html
<div class="dropdown" style="width: 100%">
<button class="btn" data-variant="secondary" data-full>Select option</button>
<div class="dropdown-menu" data-full data-open>
<button class="dropdown-item">Option A</button>
<button class="dropdown-item">Option B</button>
</div>
</div>With Vue
Use @vibe-labs/design-vue-dropdowns for <SbDropdown>, <SbDropdownMenu>, and <SbDropdownItem> which manage open/close state and map DropdownMenuStyleProps to data attributes.