More Modals Snippets
Fullscreen Menu — Animated Nav Overlay HTML CSS JS
Fullscreen Menu · Modals · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Fullscreen Menu — Clip-Path Circle Reveal with Staggered Nav Links

A fullscreen menu is the bold navigation pattern where tapping the hamburger expands a full-viewport overlay of large links — common on agency, portfolio, and editorial sites. This snippet builds it in plain HTML, CSS, and vanilla JavaScript: a clip-path circle reveal from the menu button, links that stagger in, an animated hamburger-to-X, scroll lock, and the expected close behaviours — no library.
A circular clip-path reveal
Instead of a plain fade, the overlay reveals with an expanding circle: clip-path: circle(0% at …) anchored at the menu button's position grows to circle(150% …) on open, so the menu appears to spread out from the button the user tapped. Animating clip-path is GPU-friendly and far more distinctive than opacity alone — it's the signature "wipe open from the corner" effect, and anchoring the circle at the button ties the overlay's origin to the trigger.
Staggered link entrance
The nav links don't all appear at once. Each <li> carries a custom property --i (its index), and the open state translates each link up into place with a transition-delay of base + i × step — so they cascade in one after another. Driving the stagger with a CSS custom property and calc() means the delay is data-free CSS (no per-link JavaScript), and adding a link is just another <li> with the next index. The links start translated down and clipped by their overflow: hidden parent, so they rise into view like a reveal.
Hamburger that becomes an X
The toggle's three bars animate into a close X: the top and bottom bars translate to the centre and rotate ±45°, the middle fades out. This morph is pure CSS driven by an fm-on class, and the bars switch colour to stay visible against the dark overlay. A single toggle handler opens or closes based on the current state.
Scroll lock and accessible close
Opening sets body { overflow: hidden } so the page behind can't scroll — essential for a full-viewport overlay, or the background scrolls under the menu. The overlay closes on Escape, on choosing any link, and on the toggle, all routed through one close() that also restores scroll, resets the hamburger, and updates aria-expanded/aria-hidden so the state is announced to assistive tech.
Drop-in and adaptable
It's a complete navigation overlay — wire the links to your routes, restyle the type and colours, and adjust the clip-path origin to your button's position. It's a clear reference for clip-path reveals, CSS custom-property staggering, and the scroll-lock-and-close lifecycle every fullscreen menu needs.
Step by step
How to Use
- 1Paste HTML, CSS, and JSA header with a hamburger renders; the page sits behind a hidden fullscreen overlay.
- 2Open the menuClick the hamburger — the overlay wipes open in a circle from the button and links stagger in.
- 3Close itPress Escape, click the X, or choose a link; the overlay wipes closed and scroll unlocks.
- 4Wire your linksReplace the nav items with your routes; add an <li> with the next --i to extend.
- 5Adjust the originMatch the clip-path circle's "at" position to your menu button's location.
- 6Restyle itChange the overlay colour, link type size, and stagger timing to fit your brand.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
The overlay has clip-path: circle(0% at <button position>) when closed, which clips it to nothing, and circle(150% at <same position>) when open, which expands the visible circle beyond the viewport. A CSS transition animates clip-path between the two, so the menu appears to wipe open from the button. Anchoring the circle's centre at the menu button ties the reveal's origin to the trigger.
Each <li> sets a CSS custom property --i to its index. The open state applies transition-delay: calc(base + var(--i) * step) to each link, so link 1 animates slightly before link 2, and so on — a cascade. Because the delay is computed in CSS from the index, there's no JavaScript timing per link; adding a link is just another <li> with the next --i value.
A fullscreen overlay covers the viewport, but without locking scroll the page behind it still scrolls when the user swipes or scrolls over the menu — which feels broken and can move the background out from under the links. Setting body { overflow: hidden } on open (and restoring it on close) freezes the page behind the overlay. close() always restores it so scroll never gets stuck off.
The toggle is a real button with aria-label and aria-expanded that flips on open/close, and the overlay's aria-hidden updates so assistive tech knows whether it's active. Escape closes it, matching keyboard convention. For full robustness you'd also trap focus within the open overlay and return focus to the toggle on close — a small addition on top of this foundation.
Hold an open boolean in state and toggle classes/aria from it; lock body scroll in an effect when open. In React use useState plus a useEffect for the Escape listener and scroll lock; in Vue, a ref with onMounted/onUnmounted; in Angular, a property with HostListener. The clip-path and stagger CSS are framework-agnostic — only the open state and listeners move into the framework.