|$ curl https://forge-ai.dev/api/markdown?path=docs/components/dropdowns
$cat docs/dropdowns.md
updated Recently·12 min read·published
Dropdowns
◆CSS◆HTML◆UI◆Intermediate
Dropdown menus with hover and click triggers, nested items, icons, and interactive JavaScript toggles.
Hover Dropdown
Pure CSS dropdown that opens on hover — works without JavaScript.
hover-dropdown
Live
untitled.html
HTML
| 1 | <div class="wrapper"> |
| 2 | <div class="dropdown"> |
| 3 | <button class="trigger"> |
| 4 | File ▾ |
| 5 | </button> |
| 6 | <div class="menu"> |
| 7 | <a href="#" class="item"> |
| 8 | New File |
| 9 | <span class="shortcut"> |
| 10 | ⌘N |
| 11 | </span> |
| 12 | </a> |
| 13 | <a href="#" class="item"> |
| 14 | Open... |
| 15 | <span class="shortcut"> |
| 16 | ⌘O |
| 17 | </span> |
| 18 | </a> |
| 19 | <div class="divider"> |
| 20 | </div> |
| 21 | <a href="#" class="item"> |
| 22 | Save |
| 23 | <span class="shortcut"> |
| 24 | ⌘S |
| 25 | </span> |
| 26 | </a> |
| 27 | <a href="#" class="item"> |
| 28 | Save As... |
| 29 | <span class="shortcut"> |
| 30 | ⇧⌘S |
| 31 | </span> |
| 32 | </a> |
| 33 | <div class="divider"> |
| 34 | </div> |
| 35 | <a href="#" class="item danger"> |
| 36 | Close |
| 37 | </a> |
| 38 | </div> |
| 39 | </div> |
| 40 | <div class="dropdown"> |
| 41 | <button class="trigger"> |
| 42 | Edit ▾ |
| 43 | </button> |
| 44 | <div class="menu"> |
| 45 | <a href="#" class="item"> |
| 46 | Undo |
| 47 | <span class="shortcut"> |
| 48 | ⌘Z |
| 49 | </span> |
| 50 | </a> |
| 51 | <a href="#" class="item"> |
| 52 | Redo |
| 53 | <span class="shortcut"> |
| 54 | ⇧⌘Z |
| 55 | </span> |
| 56 | </a> |
| 57 | <div class="divider"> |
| 58 | </div> |
| 59 | <a href="#" class="item"> |
| 60 | Cut |
| 61 | <span class="shortcut"> |
| 62 | ⌘X |
| 63 | </span> |
| 64 | </a> |
| 65 | <a href="#" class="item"> |
| 66 | Copy |
| 67 | <span class="shortcut"> |
| 68 | ⌘C |
| 69 | </span> |
| 70 | </a> |
| 71 | <a href="#" class="item"> |
| 72 | Paste |
| 73 | <span class="shortcut"> |
| 74 | ⌘V |
| 75 | </span> |
| 76 | </a> |
| 77 | </div> |
| 78 | </div> |
| 79 | </div> |
untitled.css
CSS
| 1 | .wrapper { |
| 2 | display:flex; |
| 3 | gap:16px; |
| 4 | align-items:flex-start; |
| 5 | justify-content:center; |
| 6 | padding:32px |
| 7 | } |
| 8 | .dropdown { |
| 9 | position:relative; |
| 10 | display:inline-flex |
| 11 | } |
| 12 | .trigger { |
| 13 | padding:10px 20px; |
| 14 | border-radius:8px; |
| 15 | font-size:13px; |
| 16 | font-weight:600; |
| 17 | font-family:system-ui, |
| 18 | sans-serif; |
| 19 | cursor:pointer; |
| 20 | border:1px solid #333; |
| 21 | background:#1A1A1A; |
| 22 | color:#E0E0E0; |
| 23 | transition:all .2s |
| 24 | } |
| 25 | .trigger:hover { |
| 26 | background:#222; |
| 27 | border-color:#00FF41 |
| 28 | } |
| 29 | .menu { |
| 30 | position:absolute; |
| 31 | top:calc(100% + 6px); |
| 32 | left:0; |
| 33 | min-width:200px; |
| 34 | background:#151515; |
| 35 | border:1px solid #333; |
| 36 | border-radius:8px; |
| 37 | padding:6px; |
| 38 | opacity:0; |
| 39 | pointer-events:none; |
| 40 | transform:translateY(-4px); |
| 41 | transition:all .2s ease; |
| 42 | z-index:20; |
| 43 | box-shadow:0 8px 24px rgba(0, |
| 44 | 0, |
| 45 | 0, |
| 46 | .4) |
| 47 | } |
| 48 | .dropdown:hover .menu { |
| 49 | opacity:1; |
| 50 | pointer-events:auto; |
| 51 | transform:translateY(0) |
| 52 | } |
| 53 | .item { |
| 54 | display:flex; |
| 55 | align-items:center; |
| 56 | justify-content:space-between; |
| 57 | padding:8px 12px; |
| 58 | border-radius:6px; |
| 59 | font-size:12px; |
| 60 | font-family:system-ui, |
| 61 | sans-serif; |
| 62 | color:#C0C0C0; |
| 63 | text-decoration:none; |
| 64 | transition:all .15s; |
| 65 | cursor:pointer |
| 66 | } |
| 67 | .item:hover { |
| 68 | background:#2A2A2A; |
| 69 | color:#E0E0E0 |
| 70 | } |
| 71 | .shortcut { |
| 72 | font-size:10px; |
| 73 | color:#666; |
| 74 | font-family:monospace |
| 75 | } |
| 76 | .divider { |
| 77 | height:1px; |
| 78 | background:#2A2A2A; |
| 79 | margin:4px 8px |
| 80 | } |
| 81 | .danger { |
| 82 | color:#EF4444 |
| 83 | } |
| 84 | .danger:hover { |
| 85 | background:rgba(239, |
| 86 | 68, |
| 87 | 68, |
| 88 | .1); |
| 89 | color:#EF4444 |
| 90 | } |
Click Toggle
Dropdown with JavaScript click toggle — stays open until clicked again or clicking outside.
click-dropdown
Live
untitled.html
HTML
| 1 | <div class="wrapper"> |
| 2 | <div class="click-dropdown"> |
| 3 | <button class="trigger" data-toggle> |
| 4 | Account ▾ |
| 5 | </button> |
| 6 | <div class="click-menu" id="account-menu"> |
| 7 | <div class="menu-header"> |
| 8 | <div class="avatar"> |
| 9 | JD |
| 10 | </div> |
| 11 | <div> |
| 12 | <div class="name"> |
| 13 | Jane Doe |
| 14 | </div> |
| 15 | <div class="email"> |
| 16 | jane@example.com |
| 17 | </div> |
| 18 | </div> |
| 19 | </div> |
| 20 | <div class="divider"> |
| 21 | </div> |
| 22 | <a href="#" class="item"> |
| 23 | <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"> |
| 24 | <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/> |
| 25 | <circle cx="12" cy="7" r="4"/> |
| 26 | </svg> |
| 27 | Profile |
| 28 | </a> |
| 29 | <a href="#" class="item"> |
| 30 | <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"> |
| 31 | <circle cx="12" cy="12" r="3"/> |
| 32 | <path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/> |
| 33 | </svg> |
| 34 | Settings |
| 35 | </a> |
| 36 | <a href="#" class="item"> |
| 37 | <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"> |
| 38 | <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/> |
| 39 | <polyline points="16 17 21 12 16 7"/> |
| 40 | <line x1="21" y1="12" x2="9" y2="12"/> |
| 41 | </svg> |
| 42 | Sign Out |
| 43 | </a> |
| 44 | </div> |
| 45 | </div> |
| 46 | <div class="click-dropdown"> |
| 47 | <button class="trigger" data-toggle> |
| 48 | Actions ▾ |
| 49 | </button> |
| 50 | <div class="click-menu"> |
| 51 | <a href="#" class="item"> |
| 52 | <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"> |
| 53 | <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/> |
| 54 | </svg> |
| 55 | Quick Action |
| 56 | </a> |
| 57 | <a href="#" class="item"> |
| 58 | <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"> |
| 59 | <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/> |
| 60 | <polyline points="14 2 14 8 20 8"/> |
| 61 | </svg> |
| 62 | Export |
| 63 | </a> |
| 64 | <a href="#" class="item"> |
| 65 | <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"> |
| 66 | <polyline points="3 6 5 6 21 6"/> |
| 67 | <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/> |
| 68 | </svg> |
| 69 | Delete |
| 70 | </a> |
| 71 | </div> |
| 72 | </div> |
| 73 | </div> |
untitled.css
CSS
| 1 | .wrapper { |
| 2 | display:flex; |
| 3 | gap:32px; |
| 4 | align-items:flex-start; |
| 5 | justify-content:center; |
| 6 | padding:32px |
| 7 | } |
| 8 | .click-dropdown { |
| 9 | position:relative; |
| 10 | display:inline-flex |
| 11 | } |
| 12 | .trigger { |
| 13 | padding:10px 20px; |
| 14 | border-radius:8px; |
| 15 | font-size:13px; |
| 16 | font-weight:600; |
| 17 | font-family:system-ui, |
| 18 | sans-serif; |
| 19 | cursor:pointer; |
| 20 | border:1px solid #333; |
| 21 | background:#1A1A1A; |
| 22 | color:#E0E0E0; |
| 23 | transition:all .2s |
| 24 | } |
| 25 | .trigger:hover { |
| 26 | background:#222; |
| 27 | border-color:#00FF41 |
| 28 | } |
| 29 | .click-menu { |
| 30 | position:absolute; |
| 31 | top:calc(100% + 6px); |
| 32 | left:0; |
| 33 | min-width:220px; |
| 34 | background:#151515; |
| 35 | border:1px solid #333; |
| 36 | border-radius:8px; |
| 37 | padding:6px; |
| 38 | display:none; |
| 39 | z-index:20; |
| 40 | box-shadow:0 8px 24px rgba(0, |
| 41 | 0, |
| 42 | 0, |
| 43 | .4) |
| 44 | } |
| 45 | .click-menu.open { |
| 46 | display:block |
| 47 | } |
| 48 | .item { |
| 49 | display:flex; |
| 50 | align-items:center; |
| 51 | gap:10px; |
| 52 | padding:8px 12px; |
| 53 | border-radius:6px; |
| 54 | font-size:12px; |
| 55 | font-family:system-ui, |
| 56 | sans-serif; |
| 57 | color:#C0C0C0; |
| 58 | text-decoration:none; |
| 59 | transition:all .15s; |
| 60 | cursor:pointer |
| 61 | } |
| 62 | .item:hover { |
| 63 | background:#2A2A2A; |
| 64 | color:#E0E0E0 |
| 65 | } |
| 66 | .divider { |
| 67 | height:1px; |
| 68 | background:#2A2A2A; |
| 69 | margin:4px 8px |
| 70 | } |
| 71 | .menu-header { |
| 72 | display:flex; |
| 73 | align-items:center; |
| 74 | gap:10px; |
| 75 | padding:8px 12px; |
| 76 | padding-bottom:10px |
| 77 | } |
| 78 | .avatar { |
| 79 | width:32px; |
| 80 | height:32px; |
| 81 | border-radius:50%; |
| 82 | background:#00FF41; |
| 83 | color:#0A0A0A; |
| 84 | display:flex; |
| 85 | align-items:center; |
| 86 | justify-content:center; |
| 87 | font-size:12px; |
| 88 | font-weight:700; |
| 89 | font-family:system-ui, |
| 90 | sans-serif |
| 91 | } |
| 92 | .name { |
| 93 | font-size:12px; |
| 94 | font-weight:600; |
| 95 | color:#E0E0E0 |
| 96 | } |
| 97 | .email { |
| 98 | font-size:10px; |
| 99 | color:#666; |
| 100 | margin-top:2px |
| 101 | } |
untitled.js
JavaScript
| 1 | document.querySelectorAll('[data-toggle]').forEach(btn=>{btn.addEventListener('click',(e)=>{e.stopPropagation();const menu=btn.nextElementSibling;menu.classList.toggle('open')})});document.addEventListener('click',()=>{document.querySelectorAll('.click-menu.open').forEach(m=>m.classList.remove('open'))});document.querySelectorAll('.click-menu').forEach(m=>{m.addEventListener('click',(e)=>e.stopPropagation())}) |
Nested Submenus
Multi-level dropdown with submenus that open on hover — pure CSS.
nested
Live
untitled.html
HTML
| 1 | <div class="wrapper"> |
| 2 | <div class="nested-drop"> |
| 3 | <button class="trigger"> |
| 4 | Menu ▾ |
| 5 | </button> |
| 6 | <div class="nested-menu"> |
| 7 | <a href="#" class="item"> |
| 8 | Dashboard |
| 9 | </a> |
| 10 | <a href="#" class="item"> |
| 11 | Analytics |
| 12 | </a> |
| 13 | <div class="has-sub"> |
| 14 | <a href="#" class="item"> |
| 15 | Reports ▸ |
| 16 | </a> |
| 17 | <div class="submenu"> |
| 18 | <a href="#" class="item"> |
| 19 | Sales Report |
| 20 | </a> |
| 21 | <a href="#" class="item"> |
| 22 | User Report |
| 23 | </a> |
| 24 | <a href="#" class="item"> |
| 25 | Error Report |
| 26 | </a> |
| 27 | <div class="divider"> |
| 28 | </div> |
| 29 | <a href="#" class="item"> |
| 30 | Custom Report |
| 31 | </a> |
| 32 | </div> |
| 33 | </div> |
| 34 | <div class="divider"> |
| 35 | </div> |
| 36 | <div class="has-sub"> |
| 37 | <a href="#" class="item"> |
| 38 | Settings ▸ |
| 39 | </a> |
| 40 | <div class="submenu"> |
| 41 | <a href="#" class="item"> |
| 42 | General |
| 43 | </a> |
| 44 | <a href="#" class="item"> |
| 45 | Security |
| 46 | </a> |
| 47 | <a href="#" class="item"> |
| 48 | Notifications |
| 49 | </a> |
| 50 | <div class="divider"> |
| 51 | </div> |
| 52 | <a href="#" class="item"> |
| 53 | Advanced |
| 54 | </a> |
| 55 | </div> |
| 56 | </div> |
| 57 | <a href="#" class="item"> |
| 58 | Help |
| 59 | </a> |
| 60 | </div> |
| 61 | </div> |
| 62 | </div> |
untitled.css
CSS
| 1 | .wrapper { |
| 2 | display:flex; |
| 3 | align-items:flex-start; |
| 4 | justify-content:center; |
| 5 | padding:32px |
| 6 | } |
| 7 | .nested-drop { |
| 8 | position:relative; |
| 9 | display:inline-flex |
| 10 | } |
| 11 | .trigger { |
| 12 | padding:10px 20px; |
| 13 | border-radius:8px; |
| 14 | font-size:13px; |
| 15 | font-weight:600; |
| 16 | font-family:system-ui, |
| 17 | sans-serif; |
| 18 | cursor:pointer; |
| 19 | border:1px solid #333; |
| 20 | background:#1A1A1A; |
| 21 | color:#E0E0E0; |
| 22 | transition:all .2s |
| 23 | } |
| 24 | .trigger:hover { |
| 25 | background:#222; |
| 26 | border-color:#00FF41 |
| 27 | } |
| 28 | .nested-menu { |
| 29 | position:absolute; |
| 30 | top:calc(100% + 6px); |
| 31 | left:0; |
| 32 | min-width:200px; |
| 33 | background:#151515; |
| 34 | border:1px solid #333; |
| 35 | border-radius:8px; |
| 36 | padding:6px; |
| 37 | opacity:0; |
| 38 | pointer-events:none; |
| 39 | transform:translateY(-4px); |
| 40 | transition:all .2s ease; |
| 41 | z-index:20; |
| 42 | box-shadow:0 8px 24px rgba(0, |
| 43 | 0, |
| 44 | 0, |
| 45 | .4) |
| 46 | } |
| 47 | .nested-drop:hover .nested-menu { |
| 48 | opacity:1; |
| 49 | pointer-events:auto; |
| 50 | transform:translateY(0) |
| 51 | } |
| 52 | .has-sub { |
| 53 | position:relative |
| 54 | } |
| 55 | .has-sub>.item { |
| 56 | display:flex; |
| 57 | align-items:center; |
| 58 | justify-content:space-between |
| 59 | } |
| 60 | .submenu { |
| 61 | position:absolute; |
| 62 | top:-6px; |
| 63 | left:calc(100% + 4px); |
| 64 | min-width:180px; |
| 65 | background:#151515; |
| 66 | border:1px solid #333; |
| 67 | border-radius:8px; |
| 68 | padding:6px; |
| 69 | opacity:0; |
| 70 | pointer-events:none; |
| 71 | transition:all .2s ease; |
| 72 | z-index:21; |
| 73 | box-shadow:0 8px 24px rgba(0, |
| 74 | 0, |
| 75 | 0, |
| 76 | .4) |
| 77 | } |
| 78 | .has-sub:hover .submenu { |
| 79 | opacity:1; |
| 80 | pointer-events:auto |
| 81 | } |
| 82 | .item { |
| 83 | display:flex; |
| 84 | align-items:center; |
| 85 | gap:10px; |
| 86 | padding:8px 12px; |
| 87 | border-radius:6px; |
| 88 | font-size:12px; |
| 89 | font-family:system-ui, |
| 90 | sans-serif; |
| 91 | color:#C0C0C0; |
| 92 | text-decoration:none; |
| 93 | transition:all .15s; |
| 94 | cursor:pointer |
| 95 | } |
| 96 | .item:hover { |
| 97 | background:#2A2A2A; |
| 98 | color:#E0E0E0 |
| 99 | } |
| 100 | .divider { |
| 101 | height:1px; |
| 102 | background:#2A2A2A; |
| 103 | margin:4px 8px |
| 104 | } |