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.

Pure CSS MIT licensed
Live Demo Open in tab
Open in playground

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">&#127959;</span>
              <span>About Us</span>
            </a>
            <a href="#" class="dd-02__link">
              <span class="dd-02__badge">&#128101;</span>
              <span>Careers</span>
            </a>
            <a href="#" class="dd-02__link">
              <span class="dd-02__badge">&#128240;</span>
              <span>Press</span>
            </a>
            <a href="#" class="dd-02__link">
              <span class="dd-02__badge">&#128205;</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">&#9889;</span><span>Analytics</span></a>
            <a href="#" class="dd-02__link"><span class="dd-02__badge">&#128202;</span><span>Reporting</span></a>
            <a href="#" class="dd-02__link"><span class="dd-02__badge">&#128736;</span><span>Integrations</span></a>
            <a href="#" class="dd-02__link"><span class="dd-02__badge">&#128274;</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">&#128196;</span><span>API Docs</span></a>
            <a href="#" class="dd-02__link"><span class="dd-02__badge">&#129520;</span><span>SDKs</span></a>
            <a href="#" class="dd-02__link"><span class="dd-02__badge">&#127381;</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; }
}

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's clip-path — keep shadows on a wrapper outside the clipped element.
  • clip-path polygon 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-radius oddly when combined with clip-path — use clip-path: inset(0 round 12px) instead of polygon + border-radius together.

Browser support

ChromeSafariFirefoxEdge
55+ 13.1+ 54+ 55+

clip-path polygon fully supported in modern browsers; Safari needed a -webkit- prefix before v13.

Search CodeFronts

Loading…