22 CSS Dropdown Menu Designs 02 / 22
Clip-Path Curtain Reveal Dropdown
A dramatic curtain-wipe dropdown where the panel is revealed by animating a clip-path polygon from a collapsed top strip to full height.
The code
<div class="dd-02">
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600&display=swap" rel="stylesheet">
<nav class="dd-02__nav">
<a href="#" class="dd-02__logo">Vertex</a>
<div class="dd-02__items">
<div class="dd-02__item">
<button class="dd-02__trigger">Company ▾</button>
<div class="dd-02__panel">
<div class="dd-02__grid">
<a href="#" class="dd-02__link">
<span class="dd-02__badge">🏗</span>
<span>About Us</span>
</a>
<a href="#" class="dd-02__link">
<span class="dd-02__badge">👥</span>
<span>Careers</span>
</a>
<a href="#" class="dd-02__link">
<span class="dd-02__badge">📰</span>
<span>Press</span>
</a>
<a href="#" class="dd-02__link">
<span class="dd-02__badge">📍</span>
<span>Locations</span>
</a>
</div>
</div>
</div>
<div class="dd-02__item">
<button class="dd-02__trigger">Platform ▾</button>
<div class="dd-02__panel">
<div class="dd-02__grid">
<a href="#" class="dd-02__link"><span class="dd-02__badge">⚡</span><span>Analytics</span></a>
<a href="#" class="dd-02__link"><span class="dd-02__badge">📊</span><span>Reporting</span></a>
<a href="#" class="dd-02__link"><span class="dd-02__badge">🛠</span><span>Integrations</span></a>
<a href="#" class="dd-02__link"><span class="dd-02__badge">🔒</span><span>Security</span></a>
</div>
</div>
</div>
<div class="dd-02__item">
<button class="dd-02__trigger">Developers ▾</button>
<div class="dd-02__panel">
<div class="dd-02__grid">
<a href="#" class="dd-02__link"><span class="dd-02__badge">📄</span><span>API Docs</span></a>
<a href="#" class="dd-02__link"><span class="dd-02__badge">🧰</span><span>SDKs</span></a>
<a href="#" class="dd-02__link"><span class="dd-02__badge">🆕</span><span>Status</span></a>
</div>
</div>
</div>
</div>
</nav>
</div> <div class="dd-02">
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600&display=swap" rel="stylesheet">
<nav class="dd-02__nav">
<a href="#" class="dd-02__logo">Vertex</a>
<div class="dd-02__items">
<div class="dd-02__item">
<button class="dd-02__trigger">Company ▾</button>
<div class="dd-02__panel">
<div class="dd-02__grid">
<a href="#" class="dd-02__link">
<span class="dd-02__badge">🏗</span>
<span>About Us</span>
</a>
<a href="#" class="dd-02__link">
<span class="dd-02__badge">👥</span>
<span>Careers</span>
</a>
<a href="#" class="dd-02__link">
<span class="dd-02__badge">📰</span>
<span>Press</span>
</a>
<a href="#" class="dd-02__link">
<span class="dd-02__badge">📍</span>
<span>Locations</span>
</a>
</div>
</div>
</div>
<div class="dd-02__item">
<button class="dd-02__trigger">Platform ▾</button>
<div class="dd-02__panel">
<div class="dd-02__grid">
<a href="#" class="dd-02__link"><span class="dd-02__badge">⚡</span><span>Analytics</span></a>
<a href="#" class="dd-02__link"><span class="dd-02__badge">📊</span><span>Reporting</span></a>
<a href="#" class="dd-02__link"><span class="dd-02__badge">🛠</span><span>Integrations</span></a>
<a href="#" class="dd-02__link"><span class="dd-02__badge">🔒</span><span>Security</span></a>
</div>
</div>
</div>
<div class="dd-02__item">
<button class="dd-02__trigger">Developers ▾</button>
<div class="dd-02__panel">
<div class="dd-02__grid">
<a href="#" class="dd-02__link"><span class="dd-02__badge">📄</span><span>API Docs</span></a>
<a href="#" class="dd-02__link"><span class="dd-02__badge">🧰</span><span>SDKs</span></a>
<a href="#" class="dd-02__link"><span class="dd-02__badge">🆕</span><span>Status</span></a>
</div>
</div>
</div>
</div>
</nav>
</div>.dd-02, .dd-02 *, .dd-02 *::before, .dd-02 *::after {
margin: 0; padding: 0; box-sizing: border-box;
}
.dd-02 ::selection { background: #f97316; color: #fff; }
.dd-02 {
--brand: #f97316;
--surface: #fff;
--text: #1c1917;
--muted: #78716c;
--border: #e7e5e4;
--hover: #fff7ed;
font-family: 'DM Sans', sans-serif;
min-height: 360px;
display: flex;
align-items: flex-start;
justify-content: center;
padding: 32px 20px;
background: linear-gradient(160deg, #fff7ed 0%, #fef3c7 100%);
}
.dd-02__nav {
display: flex;
align-items: center;
gap: 32px;
background: var(--surface);
border-bottom: 2px solid var(--border);
padding: 14px 28px;
width: 100%;
max-width: 720px;
border-radius: 12px;
box-shadow: 0 2px 16px rgba(0,0,0,.06);
position: relative;
z-index: 100;
}
.dd-02__logo {
font-size: 18px;
font-weight: 700;
color: var(--brand);
text-decoration: none;
letter-spacing: -0.5px;
margin-right: auto;
}
.dd-02__items {
display: flex;
gap: 4px;
}
.dd-02__item { position: relative; }
.dd-02__item::after {
content: "";
position: absolute;
left: 0; right: 0;
top: 100%;
height: 10px;
/* hover-bridge: invisible strip below the trigger covering
the visible gap before the panel. Lives on .__item (not
the panel, which has overflow:hidden / pointer-events:
none in its closed state) so the parent :hover stays
active while the cursor traverses the gap. */
}
.dd-02__trigger {
background: none;
border: none;
cursor: pointer;
padding: 8px 14px;
font-size: 14px;
font-weight: 500;
color: var(--text);
border-radius: 8px;
transition: background 0.15s, color 0.15s;
font-family: inherit;
}
.dd-02__trigger:hover, .dd-02__item:hover .dd-02__trigger {
background: var(--hover);
color: var(--brand);
}
/* clip-path curtain panel */
.dd-02__panel {
position: absolute;
top: calc(100% + 10px);
left: 50%;
transform: translateX(-50%);
min-width: 220px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
box-shadow: 0 16px 48px rgba(0,0,0,.12);
overflow: hidden;
clip-path: polygon(0 0, 100% 0, 100% 0, 0 0);
opacity: 0;
pointer-events: none;
transition:
clip-path 0.38s cubic-bezier(0.16, 1, 0.3, 1),
opacity 0.2s ease;
}
.dd-02__item:hover .dd-02__panel {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
opacity: 1;
pointer-events: auto;
}
.dd-02__grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4px;
padding: 8px;
}
.dd-02__link {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
border-radius: 8px;
text-decoration: none;
color: var(--text);
font-size: 13.5px;
font-weight: 500;
transition: background 0.15s;
}
.dd-02__link:hover { background: var(--hover); color: var(--brand); }
.dd-02__badge {
font-size: 16px;
line-height: 1;
}
@media (prefers-reduced-motion: reduce) {
.dd-02__panel { transition: none; }
} .dd-02, .dd-02 *, .dd-02 *::before, .dd-02 *::after {
margin: 0; padding: 0; box-sizing: border-box;
}
.dd-02 ::selection { background: #f97316; color: #fff; }
.dd-02 {
--brand: #f97316;
--surface: #fff;
--text: #1c1917;
--muted: #78716c;
--border: #e7e5e4;
--hover: #fff7ed;
font-family: 'DM Sans', sans-serif;
min-height: 360px;
display: flex;
align-items: flex-start;
justify-content: center;
padding: 32px 20px;
background: linear-gradient(160deg, #fff7ed 0%, #fef3c7 100%);
}
.dd-02__nav {
display: flex;
align-items: center;
gap: 32px;
background: var(--surface);
border-bottom: 2px solid var(--border);
padding: 14px 28px;
width: 100%;
max-width: 720px;
border-radius: 12px;
box-shadow: 0 2px 16px rgba(0,0,0,.06);
position: relative;
z-index: 100;
}
.dd-02__logo {
font-size: 18px;
font-weight: 700;
color: var(--brand);
text-decoration: none;
letter-spacing: -0.5px;
margin-right: auto;
}
.dd-02__items {
display: flex;
gap: 4px;
}
.dd-02__item { position: relative; }
.dd-02__item::after {
content: "";
position: absolute;
left: 0; right: 0;
top: 100%;
height: 10px;
/* hover-bridge: invisible strip below the trigger covering
the visible gap before the panel. Lives on .__item (not
the panel, which has overflow:hidden / pointer-events:
none in its closed state) so the parent :hover stays
active while the cursor traverses the gap. */
}
.dd-02__trigger {
background: none;
border: none;
cursor: pointer;
padding: 8px 14px;
font-size: 14px;
font-weight: 500;
color: var(--text);
border-radius: 8px;
transition: background 0.15s, color 0.15s;
font-family: inherit;
}
.dd-02__trigger:hover, .dd-02__item:hover .dd-02__trigger {
background: var(--hover);
color: var(--brand);
}
/* clip-path curtain panel */
.dd-02__panel {
position: absolute;
top: calc(100% + 10px);
left: 50%;
transform: translateX(-50%);
min-width: 220px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
box-shadow: 0 16px 48px rgba(0,0,0,.12);
overflow: hidden;
clip-path: polygon(0 0, 100% 0, 100% 0, 0 0);
opacity: 0;
pointer-events: none;
transition:
clip-path 0.38s cubic-bezier(0.16, 1, 0.3, 1),
opacity 0.2s ease;
}
.dd-02__item:hover .dd-02__panel {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
opacity: 1;
pointer-events: auto;
}
.dd-02__grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4px;
padding: 8px;
}
.dd-02__link {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
border-radius: 8px;
text-decoration: none;
color: var(--text);
font-size: 13.5px;
font-weight: 500;
transition: background 0.15s;
}
.dd-02__link:hover { background: var(--hover); color: var(--brand); }
.dd-02__badge {
font-size: 16px;
line-height: 1;
}
@media (prefers-reduced-motion: reduce) {
.dd-02__panel { transition: none; }
}How this works
The dropdown panel is always at full height in the DOM but masked by clip-path: polygon(0 0, 100% 0, 100% 0, 0 0) — a zero-height rectangle at the top edge. When the trigger is hovered, the clip-path transitions to polygon(0 0, 100% 0, 100% 100%, 0 100%), the full rectangle, revealing the content like a curtain falling open. Combined with a subtle opacity fade, the effect feels cinematic.
Because clip-path is composited by the GPU (like transform), the animation is silky-smooth at 60fps with no layout reflow. The panel uses pointer-events: none when collapsed so the clipped-out area doesn't intercept mouse events, switching to auto on hover via the parent selector.
Customize
- Try an angled wipe by using
polygon(0 0, 100% 0, 100% 100%, 0 80%)as the open state for a diagonal curtain effect. - Add a
transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1)for an expo-out ease that feels like a physical drop. - Combine with a
transform: translateY(-8px) → translateY(0)to add a slight settle bounce to the reveal. - Use
clip-path: inset(0 0 100% 0)for a simpler inset-based curtain that is slightly more readable in DevTools.
Watch out for
- Child elements with
overflow: visible(like tooltips or shadows) will be clipped by the parent'sclip-path— keep shadows on a wrapper outside the clipped element. clip-pathpolygon transitions require both states to have the same number of points; mismatched point counts cause a hard snap instead of a smooth morph.- Safari clips
border-radiusoddly when combined withclip-path— useclip-path: inset(0 round 12px)instead of polygon + border-radius together.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 55+ | 13.1+ | 54+ | 55+ |
clip-path polygon fully supported in modern browsers; Safari needed a -webkit- prefix before v13.