Component
Mega menu nav
A navigation bar with a mega menu, built on native
<details>. HTML, CSS and JavaScript — no framework,
no build step, no external request.
Original scale
The component as it was drawn. Hover over "Customers": this is the exact state of the original visual, bar the accent colour. The background is a solid black, because the surface is a translucent white veil — on white it would not read.
In context
The bar as a sticky header, with content passing behind it. Scroll the area: the white veil and the backdrop blur are of no use on a flat fill, everything happens when something moves underneath.
What passes behind
The bar's veil lets through 89% of what lies underneath, and
the backdrop blur scrambles the rest. Two settings, a single
token each: --dn-surface and
--dn-blur.
On a solid background, a white veil at 11% gives exactly
#1c1c1c — the colour measured on the original
visual. It is the same value, arrived at differently.
Without backdrop-filter, the stylesheet falls back
on that flat fill rather than leaving a dirty veil.
Keep scrolling: the bar stays, the background changes.
Variants
Two variants ship with the component. Not one rule is duplicated:
they change tokens and nothing else.
.mega-menu-nav--compact brings the bar down to form
density, .mega-menu-nav--light flips the veil for a light
page. This one also carries
data-dn-hover="off": it only opens on a click.
Opening on hover
On a device with a fine pointer, moving onto a trigger opens the
menu; leaving the bar closes it. Two delays make this usable, and they
are CSS variables: --dn-hover-in (90 ms) stops the bar
unfolding under a pointer that is only crossing it, and
--dn-hover-out (220 ms) covers the journey between the bar
and the panel — that space belongs to neither of them, so without it
the panel would vanish along the way. Moving from one menu to another,
on the other hand, is instant.
Hover replaces nothing: a click still toggles, so does the keyboard,
a touch screen is not concerned
((hover: hover) and (pointer: fine)), and as long as focus
is working inside the panel the mouse does not close it under the
fingers of whoever is using it. If you want none of it:
data-dn-hover="off" on the bar.
The staggered entrance
The panel arrives in one piece, its content follows as a wave — the
header strip, then each card, left to right and top to bottom.
Everything has settled in 370 ms. Three variables set it:
--dn-stagger-base (when it starts),
--dn-stagger-step (the gap between two blocks; at
0 everything arrives together) and
--dn-stagger-rise (the distance travelled).
The trap is worth knowing: written in pure CSS with
@starting-style, this entrance only played
once per page load. Closing a
<details> does not take its content out of the
render, the browser hides it with content-visibility — as
far as CSS is concerned the panel never stopped existing, so
@starting-style, which only fires on the first render,
never fires again. A @keyframes behaves the same way. So
the script sets the starting state by hand, for the length of one
frame, under data-dn-opening.
Without JavaScript
The component enhances <details> elements. Switch
the script off: the menus still open, from the keyboard as from the
mouse, and the name attribute keeps them mutually
exclusive. What the script adds — opening on hover, the stagger
replayed on every opening, the closing animation, Escape, clicking
outside, the arrow keys — is comfort, not function.
Open the same bar without the script →
The native event still fires
The component does not invent an event system of its own: it drives
the open attribute of <details>, so the
browser's toggle fires as usual. The log below listens to
that and nothing else — it knows nothing about Mega menu nav.
Open a menu to see the event.