CSS-Only Mega Menu
Wide panels with grouped links that open on hover and keyboard focus — every link in the HTML, no JavaScript, no menu plugin. Live demo and copy-paste code below.
- JavaScript
- 0 KB (optional 1 KB for Esc)
- Built with
:focus-within, CSS grid, container queries- Browsers
- All current browsers (Chrome, Edge, Firefox, Safari)
- Updated
Yes. A mega menu is a dropdown with a bigger panel. Put the panel inside the top-level list item, hide it with CSS, and open it with :hover for mouse users and :focus-within for keyboard users. All links stay in the HTML, so search engines see your full site structure on the first crawl.
Live demo
A real menu of this site — every link goes to a live page. Hover over Patterns, Free tools or Services, or click into the demo and press Tab. The panels show three kinds of content: plain link lists, links with a short description, and a featured item.
On a narrow screen only the top links show — each leads to an overview page.
How the CSS mega menu works
- The panel lives inside its list item
Each panel is a
<div>inside the<li>of its top-level link. Hovering or focusing anything inside the panel still counts as being inside the item, so it stays open while you move through it. - Full width from one line of CSS
The
<nav>hasposition: relativeand the list items don't. Soleft: 0; right: 0stretches every panel across the whole menu bar. - Columns without breakpoints
repeat(auto-fit, minmax(11rem, 1fr))creates as many columns as fit. Four groups sit side by side on a wide screen and wrap neatly on a smaller one. :hoverand:focus-withinopen itSame as the dropdown menu: hover for the mouse, focus for the keyboard. A 0.2 s close delay keeps the panel open if the mouse slips off it.
- Labelled groups instead of headings
Each column has a visible label, connected to its list with
aria-labelledby. Screen readers announce the group name — without adding headings to every page (see below). - Small screens get the overview pages
Under 40rem of width the panels are hidden and only the top links remain. Each one leads to a page that lists the same links. For a full mobile menu, combine it with the hamburger menu.
Copy the HTML and CSS
Exactly the code running in the demo. The links go to real pages on this site — replace them with your own. All class names start with ui-, and the menu sets its own margins and list styles, so it won't clash with your stylesheet.
<nav class="ui-mega" aria-label="CSS-first UI lab">
<ul class="ui-mega__bar">
<li class="ui-mega__item">
<a href="/lab/css-first-ui/">Patterns</a>
<div class="ui-mega__panel">
<div class="ui-mega__col">
<p class="ui-mega__label" id="ui-mega-nav">Navigation</p>
<ul aria-labelledby="ui-mega-nav">
<li><a href="/lab/css-first-ui/dropdown-menu-without-javascript/">Dropdown menu</a></li>
<li><a href="/lab/css-first-ui/css-mega-menu/" aria-current="page">Mega menu</a></li>
<li><a href="/lab/css-first-ui/hamburger-menu-without-javascript/">Hamburger menu</a></li>
<li><a href="/lab/css-first-ui/breadcrumb-navigation-schema/">Breadcrumbs</a></li>
</ul>
</div>
<div class="ui-mega__col">
<p class="ui-mega__label" id="ui-mega-structure">Page structure</p>
<ul aria-labelledby="ui-mega-structure">
<li><a href="/lab/css-first-ui/table-of-contents-anchor-links/">Table of contents</a></li>
<li><a href="/lab/css-first-ui/pagination-seo/">Pagination</a></li>
<li><a href="/lab/css-first-ui/language-switcher-hreflang/">Language switcher</a></li>
</ul>
</div>
<div class="ui-mega__col">
<p class="ui-mega__label" id="ui-mega-content">Content</p>
<ul aria-labelledby="ui-mega-content">
<li><a href="/lab/css-first-ui/accordion-without-javascript/">Accordion</a></li>
<li><a href="/lab/css-first-ui/tabs-without-javascript/">Tabs</a></li>
<li><a href="/lab/css-first-ui/read-more-without-javascript/">Read more</a></li>
<li><a href="/lab/css-first-ui/filter-without-javascript/">Filter</a></li>
</ul>
</div>
<div class="ui-mega__col">
<p class="ui-mega__label" id="ui-mega-overlays">Overlays</p>
<ul aria-labelledby="ui-mega-overlays">
<li><a href="/lab/css-first-ui/modal-without-javascript/">Modal</a></li>
<li><a href="/lab/css-first-ui/css-lightbox/">Lightbox</a></li>
<li><a href="/lab/css-first-ui/css-tooltip/">Tooltip</a></li>
</ul>
</div>
<div class="ui-mega__col">
<p class="ui-mega__label" id="ui-mega-forms">Media & forms</p>
<ul aria-labelledby="ui-mega-forms">
<li><a href="/lab/css-first-ui/css-carousel-scroll-snap/">Carousel</a></li>
<li><a href="/lab/css-first-ui/lazy-loading-images-without-javascript/">Lazy loading</a></li>
<li><a href="/lab/css-first-ui/scroll-animations-without-javascript/">Scroll animations</a></li>
<li><a href="/lab/css-first-ui/form-validation-without-javascript/">Form validation</a></li>
</ul>
</div>
</div>
</li>
<li class="ui-mega__item">
<a href="/tools/">Free tools</a>
<div class="ui-mega__panel">
<div class="ui-mega__col">
<p class="ui-mega__label" id="ui-mega-web">Website Kit</p>
<ul aria-labelledby="ui-mega-web">
<li><a href="/tools/chinese-filler-text/">Chinese dummy text</a></li>
<li><a href="/tools/chinese-font-stack-generator/">Chinese font stack</a></li>
<li><a href="/tools/pinyin-slug-generator/">Pinyin URL slugs</a></li>
<li><a href="/tools/chinese-phone-number-validator/">Phone number validator</a></li>
</ul>
</div>
<div class="ui-mega__col">
<p class="ui-mega__label" id="ui-mega-baidu">Baidu Kit</p>
<ul aria-labelledby="ui-mega-baidu">
<li><a href="/tools/baidu-serp-preview/">Baidu SERP preview</a></li>
<li><a href="/tools/chinese-seo-editor/">Chinese SEO editor</a></li>
<li><a href="/tools/baidu-keyword-combiner/">Baidu Ads keywords</a></li>
</ul>
</div>
<div class="ui-mega__col">
<p class="ui-mega__label" id="ui-mega-qa">Content QA</p>
<ul aria-labelledby="ui-mega-qa">
<li><a href="/tools/detect-chinese/">Simplified or Traditional?</a></li>
<li><a href="/tools/chinese-typography-checker/">Typography checker</a></li>
<li><a href="/tools/chinese-character-counter/">Character counter</a></li>
</ul>
</div>
<div class="ui-mega__col ui-mega__promo">
<p class="ui-mega__label">Featured</p>
<a class="ui-mega__feature" href="/tools/chinese-font-subset/">
<strong>Chinese Font Subset Generator</strong>
<span>Cut a 6 MB Chinese web font down to a few hundred KB.</span>
</a>
</div>
</div>
</li>
<li class="ui-mega__item">
<a href="/services/seo/china/">Services</a>
<div class="ui-mega__panel">
<div class="ui-mega__col">
<p class="ui-mega__label" id="ui-mega-search">Search</p>
<ul aria-labelledby="ui-mega-search">
<li><a class="ui-mega__feature" href="/services/seo/china/"><strong>Baidu SEO</strong><span>Rankings on China's largest search engine</span></a></li>
<li><a class="ui-mega__feature" href="/services/geo/china/"><strong>Chinese GEO</strong><span>Visibility in Chinese AI answers</span></a></li>
</ul>
</div>
<div class="ui-mega__col">
<p class="ui-mega__label" id="ui-mega-ads">Advertising</p>
<ul aria-labelledby="ui-mega-ads">
<li><a class="ui-mega__feature" href="/services/ppc/"><strong>Chinese PPC</strong><span>Baidu Ads campaigns and landing pages</span></a></li>
<li><a class="ui-mega__feature" href="/services/social-media-marketing/"><strong>Paid social</strong><span>Douyin, Rednote and WeChat campaigns</span></a></li>
</ul>
</div>
</div>
</li>
<li><a href="/#promise">Contact</a></li>
</ul>
</nav>/* Mega menu — no JavaScript.
Wide panels open on hover and keyboard focus.
In narrow spaces (under 40rem) only the top-level links show:
each of them leads to a page that lists the same links. */
.ui-mega {
--ui-bar: #ffffff;
--ui-text: #1b1f3b;
--ui-muted: #5d6280;
--ui-accent: #4f46e5;
--ui-hover: #eef0ff;
--ui-soft: #f5f6ff;
--ui-border: #dfe2f0;
container-type: inline-size;
position: relative; /* panels span the full width of the nav */
font: 500 0.9375rem/1.45 system-ui, -apple-system, "Segoe UI", sans-serif;
}
.ui-mega ul {
margin: 0;
padding: 0;
list-style: none;
}
.ui-mega a {
color: var(--ui-text);
text-decoration: none;
}
.ui-mega a:focus-visible {
outline: 2px solid var(--ui-accent);
outline-offset: 2px;
border-radius: 4px;
}
.ui-mega a[aria-current="page"] {
color: var(--ui-accent);
font-weight: 700;
}
/* Top bar */
.ui-mega__bar {
display: flex;
flex-wrap: wrap;
background: var(--ui-bar);
border-radius: 12px;
}
.ui-mega__bar > li > a {
display: block;
padding: 0.9rem 1.15rem;
font-weight: 600;
}
.ui-mega__bar > li > a:hover,
.ui-mega__item:focus-within > a {
color: var(--ui-accent);
background: var(--ui-hover);
}
.ui-mega__item > a::after {
content: " ▾";
font-size: 0.8em;
}
/* The panel: hidden, but every link is in the HTML */
.ui-mega__panel {
position: absolute;
top: 100%;
left: 0;
right: 0;
z-index: 20;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
gap: 1.5rem 2rem;
padding: 1.5rem 1.75rem;
background: var(--ui-bar);
border: 1px solid var(--ui-border);
border-radius: 12px;
box-shadow: 0 18px 40px rgb(15 18 40 / 0.2);
opacity: 0;
visibility: hidden;
transform: translateY(6px);
transition: opacity 0.15s, transform 0.15s, visibility 0s 0.2s;
}
/* Open on hover and on keyboard focus */
.ui-mega__item:hover > .ui-mega__panel,
.ui-mega__item:focus-within > .ui-mega__panel {
opacity: 1;
visibility: visible;
transform: none;
transition-delay: 0s;
}
/* Used by the optional Esc script — has no effect without it */
.ui-mega__item[data-closed] > .ui-mega__panel {
opacity: 0;
visibility: hidden;
}
/* Columns */
.ui-mega__label {
margin: 0 0 0.5rem;
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--ui-muted);
}
.ui-mega__col a {
display: block;
padding: 0.35rem 0;
}
.ui-mega__col a:hover {
color: var(--ui-accent);
text-decoration: underline;
}
/* Links with a description */
.ui-mega__col .ui-mega__feature {
margin: 0 -0.75rem 0.25rem;
padding: 0.6rem 0.75rem;
border-radius: 8px;
}
.ui-mega__col .ui-mega__feature:hover {
background: var(--ui-hover);
text-decoration: none;
}
.ui-mega__feature strong,
.ui-mega__feature span {
display: block;
}
.ui-mega__feature span {
font-size: 0.85rem;
font-weight: 400;
color: var(--ui-muted);
}
.ui-mega__promo {
padding: 1rem;
border-radius: 10px;
background: var(--ui-soft);
}
/* Narrow space: no panels, the top links lead to overview pages */
@container (max-width: 40rem) {
.ui-mega__panel {
display: none;
}
.ui-mega__item > a::after {
content: none;
}
}Optional: let Esc skip a panel
With CSS alone, a keyboard user tabs through every link of a panel. If your panels are large, add this small script on top. Esc then closes the panel and the next Tab goes to the next top-level item. If the script doesn't load, the menu still works. It is not active in the demo above, so the demo stays 100% JavaScript-free.
/* Optional enhancement — the menu works without it.
Esc closes the open panel and returns focus to its top link,
so keyboard users can skip a panel instead of tabbing through it. */
document.querySelectorAll('.ui-mega__item').forEach(function (item) {
var top = item.querySelector('a');
item.addEventListener('keydown', function (e) {
if (e.key !== 'Escape') return;
item.setAttribute('data-closed', '');
top.focus();
});
item.addEventListener('focusout', function (e) {
if (!item.contains(e.relatedTarget)) item.removeAttribute('data-closed');
});
item.addEventListener('mouseleave', function () {
item.removeAttribute('data-closed');
});
});Headings in a mega menu: yes or no?
Many mega menu tutorials put an <h3> above each column. We don't recommend it. The menu appears on every page, before the main content, so its headings become part of every page's heading structure — above the page's own <h1> in the source.
Screen-reader users who jump from heading to heading land in the menu first, on every page. And for search engines, the same group names repeated across the whole site say nothing about what a page is about.
The grouping itself is useful, though. A visible label plus aria-labelledby on the list keeps it: assistive technology announces “Navigation, list, 4 items” — and your heading outline stays clean.
Accessibility and browser support
| Situation | Without JS | Details |
|---|---|---|
| Mouse | Works | Opens on hover, with a short close delay for slips. |
| Keyboard | Works | Every link is reachable with Tab. A panel opens as soon as its top link gets focus. Users pass every link of an open panel — the optional script lets Esc skip it. |
| Screen readers | Works | Navigation landmark, labelled link groups, current page marked with aria-current="page". |
| Phones | Works | Panels are hidden in narrow space; the top links lead to overview pages. Nothing depends on hover. |
| Tablets, wide layout | Test it | Most touch browsers open the panel on the first tap and follow the link on the second. Test on your visitors' devices. |
| Esc to close | Needs JS | Add the optional script above. |
| Older browsers | Degrades | Without container queries the panels also open on small screens. |
aria-expanded
A common mistake in CSS-only menus is writing aria-expanded="false" on the top link. Without a script it never changes, so screen readers announce “collapsed” even while the panel is open. Either update it with JavaScript (on a <button>, not a link) or leave it out — as this demo does.
Mega menus and SEO
The good: because every panel is in the HTML, a crawler sees your complete category structure on the very first request — no hovering, no clicking, no rendering. Menus that are built or loaded with JavaScript may be partly invisible to Baidu and to AI crawlers, which mostly work with the raw HTML.
The risk: a mega menu is repeated on every page. A menu with 150 links gives every page 150 identical internal links, which spreads link value thin and makes it harder for search engines to see which pages matter most. Link to your important category pages and let those pages link deeper.
Watch the size: the menu's HTML comes before your content on every page. In our experience Baidu reads the beginning of a page's HTML most reliably, so a lean menu keeps your actual content close to the top of the source.
Questions about CSS mega menus
Can a mega menu work without JavaScript?
Yes. The panels open with :hover and :focus-within in all current browsers. JavaScript is only needed for extras like closing with Esc.
What is the difference between a mega menu and a dropdown menu?
A dropdown opens a narrow list under one item, often with further levels to the side. A mega menu opens one wide panel with several groups of links side by side, so visitors see a whole section at once. For deep hierarchies, see the multi-level dropdown menu.
Are mega menus good or bad for SEO?
Good, if the links are in the HTML and the menu stays focused on important pages. Bad, if it lists hundreds of links on every page, or if the links only appear after JavaScript runs.
How should a mega menu work on mobile?
Not as a mega menu — the panels are too wide. This demo hides them on small screens and keeps the top links, which lead to overview pages. For a menu that slides in, use the hamburger menu without JavaScript.
Should I use headings inside a mega menu?
Better not. Use visible labels connected to the lists with aria-labelledby. You keep the grouping for screen readers without adding repeated headings to every page.
This FAQ is itself built without JavaScript — see the accordion pattern.
Is your navigation helping or hurting your rankings in China?
We review menus, internal links and category structures of Chinese websites — what Baidu can crawl, which pages get the link value, and what to cut. Then we run Baidu SEO and Chinese GEO for Western companies.
CSS-First UI Patterns are written and maintained by Jademond Digital, a Chinese digital marketing agency founded in Guangzhou in 2012. We build and audit websites for Baidu, Google and AI search — which is why we care what works without JavaScript. About us →