How to Build a CSS-Only Mega Menu That Doesn’t Suck on Mobile

How to Build a CSS-Only Mega Menu That Doesn't Suck on Mobile

You have a big site with lots of content. Categories, subcategories, maybe a blog, a shop, and a few landing pages. A simple dropdown menu won’t cut it. You need a mega menu. And you need it to work on mobile without JavaScript.

The good news? It is entirely possible with modern CSS. No jQuery, no JavaScript, no extra plugins. Just clean HTML and a few smart CSS techniques. This guide walks you through building a CSS-only mega menu that stays functional, touch-friendly, and responsive on every device.

Key Takeaway

A CSS-only mega menu is built with a checkbox hack or the :focus-within pseudo-class for toggling, uses CSS Grid or Flexbox for column layout, and relies on media queries to switch from a horizontal desktop bar to a vertical accordion or off-canvas panel on mobile. No JavaScript means faster load times and fewer points of failure.

Why Go CSS-Only for a Mega Menu?

JavaScript menus have a bad habit. They break. A user with a slow connection might see the menu flash open then close. A script error can hide your navigation entirely. And JavaScript adds weight to every page load.

A CSS-only approach solves those problems. The menu is declared in your stylesheet. It either works or it doesn’t. There is no middle ground. Plus, CSS-only menus tend to score better on Core Web Vitals because there is no render-blocking script.

But there is a catch. CSS-only menus rely on specific HTML patterns to manage visibility. You cannot use onclick or event listeners. Instead, you use the :checked pseudo-class from a hidden checkbox, or the newer :focus-within pseudo-class. Both are reliable and accessible when implemented correctly.

The Core HTML Structure

Before writing any CSS, you need a solid HTML skeleton. A mega menu is just a list of lists. Here is the structure we will use:

  1. A nav wrapper with a role of “navigation”.
  2. A hidden checkbox input that acts as the toggle.
  3. A label element styled as the hamburger icon.
  4. An unordered list for the top-level items.
  5. Nested lists inside each top-level item for the mega menu columns.

The checkbox is the secret. When checked, the label’s sibling menu becomes visible. On desktop, you hide the checkbox and show the menu by default. On mobile, the checkbox controls the show/hide behavior.

Here is a simplified example:

<nav role="navigation" aria-label="Main navigation">
  <input type="checkbox" id="menu-toggle" class="menu-toggle" hidden>
  <label for="menu-toggle" class="menu-icon" aria-label="Open menu">
    <span></span>
    <span></span>
    <span></span>
  </label>
  <ul class="main-menu">
    <li><a href="#">Shop</a>
      <ul class="mega-columns">
        <li><a href="#">Clothing</a></li>
        <li><a href="#">Accessories</a></li>
        <li><a href="#">Footwear</a></li>
      </ul>
    </li>
    <li><a href="#">About</a></li>
    <li><a href="#">Contact</a></li>
  </ul>
</nav>

Notice the hidden checkbox and the label. The label is what users tap. When they tap it, the checkbox becomes checked, and the adjacent menu becomes visible using a sibling selector.

Writing the CSS: Desktop First

Start with the desktop layout. Use CSS Grid to arrange the mega menu columns. Each top-level list item that contains a sublist should expand to show a grid of links.

.main-menu {
  display: flex;
  list-style: none;
  gap: 1rem;
}

.main-menu li {
  position: relative;
}

.mega-columns {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  background: white;
  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
  min-width: 600px;
}

.main-menu li:hover .mega-columns,
.main-menu li:focus-within .mega-columns {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
  padding: 1rem;
}

The key line is the hover and focus-within selector. On desktop, hovering over a parent item reveals the mega menu. The focus-within rule ensures keyboard users can tab into the parent and see the menu too.

Making It Work on Mobile

Mobile is where most mega menus fall apart. A hover-based menu does not work on a touchscreen. You need a tap-based toggle.

This is where the checkbox trick comes in. On screens smaller than 768 pixels, hide the desktop menu and show the hamburger label. When the label is tapped, the checkbox becomes checked, and the menu becomes visible.

@media (max-width: 768px) {
  .main-menu {
    display: none;
    flex-direction: column;
    position: absolute;
    top: 100%;
    left: 0;
    width: 100%;
    background: white;
  }

  .menu-toggle:checked ~ .main-menu {
    display: flex;
  }

  .mega-columns {
    position: static;
    box-shadow: none;
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.3s ease;
  }

  .main-menu li:focus-within .mega-columns,
  .menu-toggle:checked .mega-columns {
    max-height: 500px;
  }
}

The tilde (~) is the general sibling combinator. It targets the main-menu that comes after the checked checkbox. This is the pure CSS toggle.

For the submenus inside the mobile layout, use max-height and overflow hidden. This creates a smooth accordion effect without JavaScript. Each column becomes a vertical list of links.

A Practical Checklist for Your Mega Menu

Use this list to verify your menu is ready for production.

  • The hidden checkbox has a unique ID and matches the label’s “for” attribute.
  • The label is visible on mobile and hidden on desktop.
  • The main menu is hidden by default on mobile and shown when the checkbox is checked.
  • Each mega menu column uses CSS Grid or Flexbox for equal spacing.
  • Focus styles are visible for keyboard navigation.
  • The menu works without JavaScript. Test by disabling JavaScript in your browser.
  • Touch targets are at least 44 pixels tall.
  • The menu closes when the user taps outside it (use :focus-within on the nav).

Common Mistakes and How to Fix Them

Mistake Symptom Fix
Menu flashes on load Checkbox default state is checked Ensure the checkbox is not pre-checked in HTML
Submenu does not appear on mobile The sibling selector is wrong Use ~ (tilde) not + (adjacent)
Menu is hidden on desktop Media query overrides desktop styles Put desktop styles outside the media query
Touch targets are too small Links are less than 44px tall Add padding: 0.75rem 1rem to all anchor tags
Keyboard users cannot open submenus Missing :focus-within rule Add li:focus-within .mega-columns to your selectors

Expert advice: Always test your mega menu with the browser’s developer tools. Resize the viewport and use the tab key to navigate. If you can open every submenu without a mouse, your code is solid. If not, check your focus-within selectors first.

When to Use a CSS Grid vs Flexbox for Columns

Both layout methods work. The choice depends on your content.

Use CSS Grid when your mega menu has a fixed number of columns. Grid handles equal-width columns and gaps cleanly. Use Flexbox when your columns have variable content lengths and you want them to wrap naturally.

For most mega menus, CSS Grid is the better choice. It gives you predictable column widths and makes alignment easier. Here is a quick comparison:

  • Grid: grid-template-columns: repeat(4, 1fr); creates four equal columns.
  • Flexbox: flex-wrap: wrap; with flex-basis: 25%; creates a similar layout but wraps items differently.

Stick with Grid for mega menus. It is more predictable for multi-column layouts.

Improving Accessibility Without JavaScript

A CSS-only menu can still be accessible. Follow these guidelines:

  • Use aria-label on the nav and the hamburger label.
  • Ensure the checkbox input is not focusable on desktop. Use display: none only on the label, not the input.
  • Add aria-expanded via CSS? No, you cannot. But you can use the :checked state to style a visual indicator. For example, change the hamburger icon to an X when the menu is open.
  • Provide a skip link at the top of the page for keyboard users.

For a deeper look at accessible navigation patterns, read our guide on building accessible forms that work for everyone. The same principles apply to menus.

Putting It All Together in a Real Project

Here is a complete workflow you can follow:

  1. Write the HTML structure with a hidden checkbox, a label, and nested lists.
  2. Style the desktop layout first. Use Flexbox for the top bar and CSS Grid for the mega columns.
  3. Add hover and focus-within selectors to reveal submenus.
  4. Write the mobile media query. Hide the desktop menu. Show the hamburger label.
  5. Use the checked pseudo-class to show the menu on mobile.
  6. Set max-height transitions on the submenus for a smooth accordion effect.
  7. Test with keyboard navigation and on a real phone.

This exact workflow is used in the tutorial building a pure CSS accordion component that is accessible and lightweight. The same principles apply to mega menus.

Troubleshooting a Stubborn Menu

If your menu does not behave, check these three things first.

The checkbox is not being checked. This happens when the label does not match the checkbox ID. Double check both values.

The sibling selector is wrong. The general sibling combinator ~ targets any sibling that comes after the element. The adjacent combinator + targets only the immediate next sibling. Use ~ for the main menu.

The media query is overriding desktop styles. Move your desktop styles above the media query. CSS reads top to bottom. If the media query comes first, desktop styles will override it.

For more help with responsive breakpoints, read the beginner’s guide to responsive breakpoints.

Your Turn to Build

Building a CSS-only mega menu is not about avoiding JavaScript. It is about building something that loads fast, works everywhere, and does not break. The checkbox hack is simple. The focus-within selector is modern. Together they give you a menu that works on desktop, mobile, and everything in between.

Start with the HTML skeleton. Style the desktop layout. Add the mobile toggle. Test with your own fingers, not just your mouse. When it works, you will have a navigation system that feels solid and professional.

If you run into issues, revisit the checklist and the troubleshooting table. And remember, every mega menu starts as a simple list. Yours is no different.

Posted in CSS     

Leave a Reply

Your email address will not be published. Required fields are marked *