Dropdown Menu
Popover menu with keyboard navigation, submenus and destructive items. Triggers via popovertarget.
INSTALL
1pnpm dlx @sayagodev/mo add dropdownOr import the files from the package (no copy):
1import "@sayagodev/mo/css/dropdown.css";2import "@sayagodev/mo/css/shared.css";3import "@sayagodev/mo/js/dropdown.js";
EXAMPLES
Demo
1<mo-dropdown>2 <button type="button" popovertarget="dd-demo" class="outline">Open</button>3 <menu popover id="dd-demo">4 <div data-label>My Account</div>5 <button role="menuitem" type="button">6 Billing7 <span data-shortcut>⌘B</span>8 </button>9 <button role="menuitem" type="button" disabled>10 Credits11 <span data-shortcut>⌘C</span>12 </button>13 <hr />14 <button role="menuitem" type="button" popovertarget="dd-demo-sub">15 Invite users16 </button>17 <menu popover id="dd-demo-sub">18 <button role="menuitem" type="button">Email</button>19 <button role="menuitem" type="button">Message</button>20 <hr />21 <button role="menuitem" type="button">More…</button>22 </menu>23 <hr />24 <button role="menuitem" type="button">Keyboard shortcuts</button>25 <button role="menuitem" type="button">API</button>26 <hr />27 <button role="menuitem" type="button">Team</button>28 <button role="menuitem" type="button">New team</button>29 <hr />30 <button role="menuitem" type="button" data-variant="danger">31 Log out32 <span data-shortcut>⇧⌘Q</span>33 </button>34 </menu>35</mo-dropdown>36
Basic with submenu
1<mo-dropdown>2 <button type="button" popovertarget="dd-basic" class="outline">Open</button>3 <menu popover id="dd-basic">4 <button role="menuitem" type="button">Team</button>5 <button role="menuitem" type="button" popovertarget="dd-basic-sub">6 Invite users7 </button>8 <menu popover id="dd-basic-sub">9 <button role="menuitem" type="button">Email</button>10 <button role="menuitem" type="button">Message</button>11 <hr />12 <button role="menuitem" type="button">More…</button>13 </menu>14 <button role="menuitem" type="button">15 New Team16 <span data-shortcut>⌘+T</span>17 </button>18 </menu>19</mo-dropdown>20
With icons
1<mo-dropdown>2 <button type="button" popovertarget="dd-icons" class="outline">Open</button>3 <menu popover id="dd-icons">4 <button role="menuitem" type="button">5 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" 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>6 Profile7 </button>8 <button role="menuitem" type="button">9 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" 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" /><line x1="2" x2="22" y1="10" y2="10" /></svg>10 Billing11 </button>12 <button role="menuitem" type="button">13 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" 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>14 Settings15 </button>16 <hr />17 <button role="menuitem" type="button" data-variant="danger">18 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" 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" /><polyline points="16 17 21 12 16 7" /><line x1="21" x2="9" y1="12" y2="12" /></svg>19 Log out20 </button>21 </menu>22</mo-dropdown>23
With shortcuts
My Account
1<mo-dropdown>2 <button type="button" popovertarget="dd-shortcuts" class="outline">Open</button>3 <menu popover id="dd-shortcuts">4 <div data-label>My Account</div>5 <button role="menuitem" type="button">6 Profile7 <span data-shortcut>⇧⌘P</span>8 </button>9 <button role="menuitem" type="button">10 Billing11 <span data-shortcut>⌘B</span>12 </button>13 <button role="menuitem" type="button">14 Settings15 <span data-shortcut>⌘S</span>16 </button>17 <hr />18 <button role="menuitem" type="button">19 Log out20 <span data-shortcut>⇧⌘Q</span>21 </button>22 </menu>23</mo-dropdown>24
Checkboxes
Status
1<mo-dropdown>2 <button type="button" popovertarget="dd-check" class="outline">Open</button>3 <menu popover id="dd-check">4 <div data-label>Status</div>5 <button role="menuitemcheckbox" type="button" aria-checked="true">6 Active7 </button>8 <button role="menuitemcheckbox" type="button" aria-checked="false">9 Paused10 </button>11 <button role="menuitemcheckbox" type="button" aria-checked="false">12 Archived13 </button>14 </menu>15</mo-dropdown>16
Checkboxes with icons
1<mo-dropdown>2 <button type="button" popovertarget="dd-check-icons" class="outline">Open</button>3 <menu popover id="dd-check-icons">4 <button role="menuitemcheckbox" type="button" aria-checked="true">5 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="18" height="18" x="3" y="3" rx="2" /><path d="M9 17V7" /></svg>6 Show Sidebar7 </button>8 <button role="menuitemcheckbox" type="button" aria-checked="false">9 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="18" height="18" x="3" y="3" rx="2" /><path d="M15 9v6" /></svg>10 Show Toolbar11 </button>12 <button role="menuitemcheckbox" type="button" aria-checked="false">13 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="18" height="18" x="3" y="3" rx="2" /><path d="M8 12h8" /></svg>14 Centered Layout15 </button>16 </menu>17</mo-dropdown>18
Radio group
Panel Position
1<mo-dropdown>2 <button type="button" popovertarget="dd-radio" class="outline">Open</button>3 <menu popover id="dd-radio">4 <div data-label>Panel Position</div>5 <button role="menuitemradio" type="button" aria-checked="true">Top</button>6 <button role="menuitemradio" type="button" aria-checked="false">Bottom</button>7 <button role="menuitemradio" type="button" aria-checked="false">Right</button>8 </menu>9</mo-dropdown>10
Destructive
1<mo-dropdown>2 <button type="button" popovertarget="dd-destructive" class="outline">Actions</button>3 <menu popover id="dd-destructive">4 <button role="menuitem" type="button">5 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" /><path d="m15 5 4 4" /></svg>6 Edit7 </button>8 <button role="menuitem" type="button">9 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" /><polyline points="16 6 12 2 8 6" /><line x1="12" x2="12" y1="2" y2="15" /></svg>10 Share11 </button>12 <hr />13 <button role="menuitem" type="button" data-variant="danger">14 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18" /><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></svg>15 Delete16 </button>17 </menu>18</mo-dropdown>19
Avatar menu
1<mo-dropdown>2 <button type="button" popovertarget="dd-avatar" class="ghost icon" aria-label="Open account menu">3 <figure data-variant="avatar">LR</figure>4 </button>5 <menu popover id="dd-avatar">6 <button role="menuitem" type="button">7 Account8 <span data-shortcut>⇧⌘A</span>9 </button>10 <button role="menuitem" type="button">11 Billing12 <span data-shortcut>⌘B</span>13 </button>14 <hr />15 <button role="menuitem" type="button" data-variant="danger">16 Log out17 <span data-shortcut>⇧⌘Q</span>18 </button>19 </menu>20</mo-dropdown>21
Complex
File
Recent Projects
View
1<mo-dropdown>2 <button type="button" popovertarget="dd-complex" class="outline">Complex Menu</button>3 <menu popover id="dd-complex">4 <div data-label>File</div>5 <button role="menuitem" type="button">6 New File7 <span data-shortcut>⌘N</span>8 </button>9 <button role="menuitem" type="button">10 New Folder11 <span data-shortcut>⇧⌘N</span>12 </button>13 <hr />14 <button role="menuitem" type="button" popovertarget="dd-complex-sub">15 Open Recent16 </button>17 <menu popover id="dd-complex-sub">18 <div data-label>Recent Projects</div>19 <button role="menuitem" type="button">Project Alpha</button>20 <button role="menuitem" type="button">Project Beta</button>21 <hr />22 <button role="menuitem" type="button" popovertarget="dd-complex-sub2">More Projects</button>23 <menu popover id="dd-complex-sub2">24 <button role="menuitem" type="button">Project Gamma</button>25 <button role="menuitem" type="button">Project Delta</button>26 </menu>27 <hr />28 <button role="menuitem" type="button">Browse…</button>29 </menu>30 <hr />31 <div data-label>View</div>32 <button role="menuitemcheckbox" type="button" aria-checked="true">33 Show Sidebar34 </button>35 <button role="menuitemcheckbox" type="button" aria-checked="false">36 Show Toolbar37 </button>38 <hr />39 <button role="menuitemradio" type="button" aria-checked="true">Light Theme</button>40 <button role="menuitemradio" type="button" aria-checked="false">Dark Theme</button>41 </menu>42</mo-dropdown>43
USAGE
1<mo-dropdown>2 <button popovertarget="m1">Options</button>3 <menu popover id="m1">4 <div data-label>Group label</div>5 <button role="menuitem">Item <span data-shortcut>⌘N</span></button>6 <button role="menuitemcheckbox" aria-checked="true">Toggle</button>7 <hr />8 <button role="menuitem" popovertarget="s1">Submenu…</button>9 <menu popover id="s1">10 <button role="menuitem">Nested item</button>11 </menu>12 </menu>13</mo-dropdown>
API
NAMEKINDDESCRIPTION
<mo-dropdown>elementPositioning, keyboard nav, focus return for the whole tree
popovertargetattributeOpens menus via Popover API; inside a menu it opens a side submenu
[role=menuitem*]attributeitem · menuitemcheckbox · menuitemradio (aria-checked toggles)
[data-label]attributeNon-interactive group heading
[data-shortcut]attributeRight-aligned key hint
[data-variant=danger]attributeDestructive item styling
--mo-dropdown-min-width / -radiustokenSurface hooks
FILES
src/css/dropdown.csssrc/css/shared.csssrc/js/dropdown.jsInstall:
mo add dropdownTHEMING · PLAYGROUND
This is the exact component from the examples above, wearing a completely different theme. Edit the tokens and --mo-* hooks in the editor — autocomplete knows them all — and the preview obeys in real time. Your CSS always wins.
Global tokens
--accent--accent-foreground--border--destructive--foreground--muted-foreground--popover--popover-foreground--radius--ringComponent hooks
--mo-dropdown-min-width--mo-dropdown-radiusPreview
Your CSS
Try it: type --mo- for the component hooks, or edit any value — the preview obeys in real time.