Entry 04

Reveal with clip-path

Use it for dropdowns, menus and panels that open over the page and whose height is not known. It unrolls the panel without animating height, and it works in every browser.

Open
Open and close the menu. It unrolls downward at its full size. Its shadow shows at the sides and the bottom and not along the top edge, and the text under it does not move. While it is closed, its links cannot be clicked or reached with Tab.
Code CSS · 17 lines
.menu {
  clip-path: inset(0 -3rem 100%);
  visibility: hidden;
}

.menu.is-open {
  clip-path: inset(0 -3rem -3rem);
  visibility: visible;
}

@media (prefers-reduced-motion: no-preference) {
  .menu {
    transition:
      clip-path 0.25s var(--ease-out, ease-out),
      visibility 0.25s;
  }
}
Why it works, the rules and the credits 5 reasons · 3 rules · support · credits

Why it works

  1. inset() clips from each edge, in the order top, sides, bottom. A bottom value of 100% hides the whole box. The percentage refers to the element's own height, so nothing has to measure it.
  2. A negative inset widens the clip beyond the box. That leaves room for the shadow at the sides and the bottom. The top stays at 0, so no shadow shows where the menu meets its trigger.
  3. Layout never changes. The panel has its full size the whole time and nothing around it moves.
  4. The clipped part takes no pointer events, so a closed menu cannot be clicked.
  5. visibility in the transition switches to hidden only when the close has finished. That takes the links out of the tab order.

Rules

  1. The negative inset must be larger than the shadow's blur plus its offset.
  2. Use it for panels that overlap the page. A closed panel in the normal flow leaves a gap, so an accordion uses Accordion that animates its height.
  3. Add round with a radius inside inset() only when the clip itself should have round corners. The element's own border-radius still applies.

Support

Every browser.

Credits

Borrowed from: Stripe, in the navigation menu on stripe.com. The visibility part was added for this list.

Docs: MDN: inset()