Elements Dropdowns
Elements

Dropdowns


Setup

Dropdown styles are included in Manifest CSS or a standalone stylesheet, both referencing theme variables.

Dropdown functionality is included in manifest.js with all core plugins, or it can be selectively loaded.

<!-- Manifest CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mnfst@latest/lib/manifest.code.min.css" />

<!-- Manifest JS -->
<script src="https://cdn.jsdelivr.net/npm/mnfst@latest/lib/manifest.min.js"></script>

For OS dropdowns, see selects.


Default

Dropdowns use the <menu> element as a popover. The <button> that opens the dialog requires the x-dropdown attribute, matching the menu ID.

<button x-dropdown="basic-menu">Open Menu</button>

<menu popover id="basic-menu">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
</menu>

Hover

Add the hover modifier to x-dropdown for mouseover dropdowns:

<button x-dropdown.hover="hover-menu">Hover Me</button>

<menu popover id="hover-menu">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
</menu>

Hover dropdowns include a small delay to prevent accidental auto-close if the mouse briefly leaves the trigger or menu area.


Context Menu

Add the context modifier to x-dropdown for right-click dropdowns. The menu appears at the cursor position, replacing the browser's native context menu on the trigger element.

<div x-dropdown.context="context-menu">Right-click this area</div>

<menu popover id="context-menu">
    <li>Cut</li>
    <li>Copy</li>
    <li>Paste</li>
</menu>

The trigger element does not need to be a <button> since the menu is opened programmatically on right-click rather than via the popovertarget attribute's click-to-toggle behavior. The menu still uses the Popover API for rendering and stacking.


Nesting

Create multi-level navigation menus with nested dropdowns.

<button x-dropdown="nested-menu">Main Menu</button>

<!-- Main Menu -->
<menu popover id="nested-menu">
    <li>Item 1</li>
    <li>Item 2</li>
    <button x-dropdown="submenu-1"><span>Submenu</span><span x-icon="lucide:chevron-right" class="trailing"></span></button>
    <li>Item 4</li>
    
    <!-- Submenu 1 -->
    <menu popover id="submenu-1">
        <li>Item 1</li>
        <li>Item 2</li>
        <button x-dropdown.hover="submenu-2"><span>Hover Submenu</span><span x-icon="lucide:chevron-right" class="trailing"></span></button>
        
        <!-- Submenu 2 -->
        <menu popover id="submenu-2">
            <li>Item 1</li>
            <li>Item 2</li>
            <li>Item 3</li>
        </menu>

    </menu>
</menu>

Nested dropdowns automatically position themselves to avoid overlapping and maintain proper navigation flow.


Positioning

Menus have utility classes like top and bottom to position them in relation to their trigger buttons. If no class is set, menus default to bottom-start, or end-start if nested.

Examples
<!-- Top -->
<menu popover id="..." class="top">...</menu>

<!-- Bottom with start alignment -->
<menu popover id="..." class="bottom-start">...</menu>

<!-- Start with top alignment -->
<menu popover id="..." class="start-top">...</menu>

<!-- Top start corner (either version works) -->
<menu popover id="..." class="top-start-corner">...</menu>
<menu popover id="..." class="start-top-corner">...</menu>

Regardless of a set class, dropdowns overflowing the viewport will attempt to stay onscreen with default fallback positions.


Templating

HTML IDs must identify single elements on a page, and generating multiple dropdowns in a template loop requires each dropdown be assigned a unique ID. These can be generated with Alpine using template literals like ${i}.

1234567891011121314151617
<template x-for="i in 3" :key="i">

    <!-- Multiple elements need to be wrapped in a container, since template tags only recognize their first child. -->
    <div>

        <!-- Button template -->
        <button x-dropdown="`template-menu-${i}`" x-text="`Menu ${i}`"></button>
        
        <!-- Menu template -->
        <menu popover :id="`template-menu-${i}`">
            <li>Item 1</li>
            <li>Item 2</li>
        </menu>

    </div>

</template>

Content

Use <li> elements for generic options in a dropdown, with Alpine's @click directive giving them utility. A variety of other elements support additional dropdown content needs.

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
<button x-dropdown="content-menu"><span>Dropdown</span><span x-icon="lucide:chevron-down" class="trailing"></span></button>

<menu popover id="content-menu" class="w-60 max-h-160">

    <small>List Items</small>
    <li @click="alert('Hello world')">Do Something</li>
    <li><span x-icon="lucide:house"></span><span>Icon</span></li>
    <li><span>Trailing</span><kbd class="trailing"></kbd><kbd>D</kbd></li>
    <li class="brand">Brand</li>
    <li class="accent">Accent</li>
    <li class="negative">Negative</li>

    <hr>

    <small>Links</small>
    <a href="#"><span x-icon="lucide:home"></span>Home</a>
    <a href="#"><span x-icon="lucide:settings"></span><span>Settings</span><span x-icon="lucide:external-link" class="trailing"></span></a>

    <hr>

    <small>Buttons</small>
    <button><span x-icon="lucide:copy"></span><span>Copy</span></button>
    <button><span x-icon="lucide:edit"></span><span>Edit</span></button>

    <hr>

    <small>Checkboxes</small>
    <label><input type="checkbox" /><span>Lorem ipsum dolar sit amet</span></label>
    <label><input type="checkbox" /><span>Consectetur adipiscing elit</span></label>

    <hr>

    <small>Radios</small>
    <label><input type="radio" id="option-a" name="group-preview" checked /><span>Option A</span></label>
    <label><input type="radio" id="option-b" name="group-preview" /><span>Option B</span></label>
    <label><input type="radio" id="option-c" name="group-preview" /><span>Option C</span></label>

    <hr>

    <small>Switches</small>
    <label for="switch">Switch 1<input id="switch" role="switch" type="checkbox" checked /></label>
    <label for="switch">Switch 2<input id="switch" role="switch" type="checkbox"/></label>

    <hr>

    <small>Text Inputs</small>
    <input placeholder="Text input" />
    <textarea placeholder="Textarea"></textarea>

</menu>

Use the following elements as direct chilren of a dropdown <menu>:

  • <small> - Group titles
  • <hr> - Dividers
  • <li> - Generic options
  • <button> - Button options (i.e. triggers for sub-dropdowns)
  • <a> - Links
  • <label> - Wrappers for radios, checkboxes, and switches
  • <input> - Single line text fields
  • <textarea> - Multi-line text fields

And use <span> within applicable elements above for icons, truncating text with ellipsis, and trailing content.


Styles

Theme

Default dropdowns use the following theme variables:

Variable Purpose
--color-popover-surface Menu background color
--color-content-stark Menu text color
--color-field-surface Hover background color
--color-content-neutral Section title color
--color-line Divider color
--spacing-popover-offset Offset from trigger element

Tailwind CSS

If using Tailwind, individual menus can be customized with utility classes. Menus taller than a max height will vertically scroll.

<button x-dropdown="menu-wide-preview"">Offset & Widen</button>
<menu popover id="menu-wide-preview" class="w-100 !m-6">
    <li>Lorem ipsum dolar sit amet</li>
    <li>Consectetur adipiscing elit</li>
    <li>Sed do eiusmod tempor incididunt</li>
</menu>

Customization

Modify base dropdown styles with custom CSS for the menu[popover] selector.

menu[popover] {
    background-color: #f0f8ff;
    border: 2px solid #3b82f6;
    border-radius: 12px;
    box-shadow: 0 8px 25px rgba(59, 130, 246, 0.3);

    /* Any relevant options */
    & :where(li, a, button, label) {
        color: #1e40af;
        border-radius: 8px;

        &:hover {
            background-color: #dbeafe;
        }
    }
}

Powered by Manifest