13 Pure CSS7 Light JSPublished 14 new designs · Updated on
A mega menu is a panel-layout problem before it is a styling problem. These 20 hand-coded mega menus cover hover-intent delay, the safe-triangle fix for diagonal pointer travel, multi-column grid panels, viewport-edge collision, keyboard access with Escape and focus return, correct disclosure ARIA rather than role="menu", the touch double-tap trap, and mobile collapse. Every demo is scoped under its own .mm-NN class, guards motion behind prefers-reduced-motion, and is framework-agnostic copy-paste code.
You just browsed 20 mega menus. Nucleo is a library of 44,000+ SVG icons in five families (Core, UI, Sharp, Pixel and Micro Bold), with web, Mac and Windows apps to manage them. The All Icons Bundle is $149 with lifetime updates.
The reference build the rest of this collection borrows from: a <nav> with an accessible name, top-level items as real <button> elements carrying aria-expanded and aria-controls, and a panel that opens on :hover and :focus-within so a keyboard user gets the same affordance a mouse user does. Drop-in product header, panel and page surface, with no script anywhere.
Opening on raw mouseenter means a pointer crossing the bar to reach something else fires three panels in sequence. This airline header ships the fix: a 120ms open delay that cancels if the pointer leaves, and a 250ms close delay so a brief slip does not dismiss an open panel. Drag the pointer straight across the bar and only the item you stop on opens.
Moving diagonally from a nav item to the far corner of its panel passes over sibling triggers, and a naive handler closes the panel mid-journey. This fleet console tracks the pointer's trajectory and keeps the panel open while movement stays inside the triangle between the cursor and the panel's two near corners. Aim for the bottom-right link and the panel holds.
The panel treated purely as a grid problem, on a publishing workspace that has genuinely uneven sections. minmax(0, 1fr) stops one long link from blowing out a track, subgrid lines the column footers up across sections of different lengths, and repeat(auto-fit, …) makes the column count follow the content instead of a media query per breakpoint.
The panel that hangs off the last nav item, where a left-aligned panel runs straight off the right edge of the viewport. This documentation header anchors from the trailing edge and clamps with max-inline-size, then upgrades to CSS Anchor Positioning with position-try-fallbacks behind an @supports gate so Chromium flips automatically and everything else keeps the clamp.
The header content is capped at a max-width but the mega panel should span the whole viewport, and doing that from inside a constrained parent is the actual layout puzzle. This garden retail header solves it by moving the containing block rather than the panel — the outer element is positioned, the inner row keeps its cap, and no viewport units or negative margins are involved.
Newsletter
Enjoying these demos? Get new ones by email.
A short email when a new collection ships. Unsubscribe any time.
Everything :focus-within cannot do, on a dark application shell where keyboard users live. <kbd>Escape</kbd> closes the panel and returns focus to its trigger, <kbd>Tab</kbd> moves through the panel and closes it when focus leaves the item entirely, and <kbd>Enter</kbd> or <kbd>Space</kbd> toggles. One state object drives both the class and aria-expanded, so the attribute never lies.
Whether arrow keys belong in a mega menu, and how far to take them. <kbd>←</kbd> and <kbd>→</kbd> move between top-level items with a roving tabindex, <kbd>↓</kbd> enters the open panel, <kbd>Escape</kbd> reverses it. Inside the panel the links stay in the ordinary tab order, because forcing a 2D grid model onto columns of links usually makes things worse.
The boundary marker for the whole collection, shipped as a working retail header. role="menu" and role="menuitem" belong to application menus and bring keyboard conventions that are wrong for navigation links. This panel is a disclosure instead: a <button> with aria-expanded and aria-controls, a named region, real lists, real links and aria-current="page" on the active one.
On touch, a hover-opened menu whose trigger is also a link needs two taps — the first opens the panel, the second follows the link — and users almost never discover that. This mobile storefront separates the jobs: the trigger is a <button> that only opens, the section's own destination is the first link inside the panel, and pointerdown branches on pointerType so hybrid laptops behave too.
Opening a large panel is a visual change that assistive technology also has to perceive. aria-expanded covers the state, but a panel with dozens of links needs an accessible name and a real heading structure so a user can navigate it by heading instead of tabbing through everything. This university header ships all three, plus a visually hidden count so people know what they are entering.
Instead of one panel per nav item, a single container swaps its contents as the active item changes. That keeps the open and close animation continuous when moving between items rather than closing and reopening, and it is what most large sites actually ship. Built here as a streaming service header, with the sections pre-rendered and toggled through the hidden attribute so focus survives every swap.
An element entering the DOM or coming out of display: none has no previous state to transition from, which is why panel entry animations historically needed a keyframe or a forced reflow. @starting-style declares that before-state and transition-behavior: allow-discrete lets display flip at the end of the exit, so this coffee subscription panel genuinely leaves the layout and still fades out.
A multi-column panel cannot survive a 360px viewport, and shrinking it produces an unusable wall of links. Below the breakpoint it becomes a different component: an accordion inside a drawer, where each top-level item expands its section in place. Both forms of the same clinic navigation are shipped here — the desktop panel and the phone drawer — from one markup pattern.
Newsletter
Enjoying these demos? Get new ones by email.
A short email when a new collection ships. Unsubscribe any time.
Publication navigation: section columns beside a featured article card with an image, an eyebrow and a headline. The image column is the real layout challenge — an unconstrained image dictates the panel's height, so the card is given a fixed aspect ratio and the link columns are allowed to set the height instead. Rebuilt from the ground up for v2.
Product navigation carrying an operational column: service health rows, the current incident line, and deploy state, sitting beside the ordinary link columns. The trap this demo is built around is that live-updating content inside an open panel must not shift layout under the pointer — every row is fixed-height, every number is tabular, and the longest possible value reserves its own space.
A category grid where each destination is an image card rather than a text link, for the case where the picture carries more information than the label. The demo is built around image loading: an unsized image inside a panel that opens on hover causes a visible reflow the first time it appears, so every card reserves its box before the file arrives.
The dense case: many departments across four columns, each with its own sub-links. This is where column layout and scannability are genuinely hard, and where the shared-panel approach from demo 12 starts to earn its place. A strict typographic grid, ruled columns and a deliberate ceiling on how much any one column may hold.
A panel containing controls rather than only links. The interaction trap is specific: a control inside a hover-opened panel must not close it, and real inputs need the panel to stay open on click, on drag and while a keyboard user moves between fields. Built with genuine <input> elements, no surrounding form, and a live result count.
Commerce navigation with a promotional block beside the links. The design constraint is the interesting part: the promo has to stay subordinate to the navigation, because a panel that is mostly marketing stops doing the job the user opened it for. One offer, one third of the panel, and the categories keep their weight.
FAQ
Frequently asked questions
When should I use a mega menu instead of a plain dropdown?
Less often than you would think. A mega menu earns its complexity when navigation has roughly twenty or more destinations a visitor genuinely needs surfaced rather than buried — a retailer's department taxonomy, a documentation site's product surface, a publisher's section list. Below about that threshold, a plain dropdown or even a flat nav bar serves people better: it opens faster, needs no hover-intent tuning, has no safe-triangle problem, and degrades to a mobile menu without becoming a different component. Reach for a mega panel when the breadth is real, not because it looks substantial.
Can I build a mega menu with no JavaScript at all?
Partly. The colon-focus-within selector opens a panel for both mouse and keyboard users with zero script, and thirteen of the twenty demos here are pure CSS on exactly that basis. What CSS cannot do is close a panel on Escape, return focus to the trigger afterwards, run a hover-intent delay so the panel does not flicker as a pointer crosses the bar, or move focus with arrow keys. Those need a listener. So a pure-CSS mega menu is a legitimate starting point rather than a finished component — the seven Light JS demos here add each missing piece in under thirty lines of vanilla code with a working cleanup path.
Should the panel use role=menu and role=menuitem?
No. Those roles belong to application menus — the kind you find in a desktop editor's menu bar — and they carry keyboard conventions that are wrong for site navigation. Applying them tells a screen-reader user to expect a menu widget driven by arrow keys, then hands them a list of ordinary links. The correct pattern is a disclosure: a real button element carrying aria-expanded and aria-controls pointing at the panel, ordinary anchors inside, and aria-current='page' on the active one. One demo in this collection is dedicated to that boundary and shows what each markup actually announces.
Why does my mega menu flicker when the pointer crosses the nav bar?
Because it opens on a raw pointer-enter event. Moving from one side of the bar to the other passes over every trigger in between, and each one fires, so three or four panels open and close in sequence. The fix is hover intent: a short open delay of roughly 120ms that cancels if the pointer leaves before it elapses, paired with a longer close delay of around 250ms so a brief slip off the trigger does not dismiss an open panel. A related failure is the diagonal one — moving toward the far corner of an open panel crosses sibling triggers and closes it mid-journey. Two demos here cover the delay approach and the safe-triangle approach separately.
How does a mega menu work on a phone?
It stops being a mega menu. A multi-column panel cannot survive a 360px viewport, and shrinking it produces an unusable wall of links. Below the breakpoint the honest answer is a different component: an accordion inside a drawer, where each top-level item expands its own section in place. Three things change at that line — hover intent switches off because there is no hover, aria-expanded starts describing an accordion rather than a panel, and focus management inside the drawer becomes mandatory rather than optional. One demo shows the desktop panel and the mobile accordion side by side with the breakpoint marked.
How do I stop the panel from pushing my page content down when it opens?
Take it out of flow. A panel left in normal document flow adds its own height to the header the moment it becomes visible, so everything below jumps — the single most common visual defect in a hand-built mega menu. Position the panel absolutely against a positioned ancestor, give that ancestor a stacking context, and animate opacity and transform rather than height. Two related traps are worth knowing: an unsized image inside the panel causes a reflow on first open, so give images explicit dimensions or an aspect ratio; and live-updating content inside an open panel must not change row heights under the pointer.