If you’re building a modern website in 2026, a responsive navigation menu is non-negotiable. Visitors arrive from phones, tablets, laptops, and even foldable devices, and your menu has to look great and work flawlessly on every single one of them.
The good news? You don’t need a heavy JavaScript framework or a third-party library to build one. In this tutorial, we’ll build a clean, accessible, and fully responsive navigation menu using CSS and HTML only, including a working hamburger toggle. Just copy, paste, and customize.
What You’ll Build
By the end of this guide, you’ll have a navigation bar that:
- Displays as a horizontal menu on desktop screens
- Collapses into a hamburger icon on mobile devices
- Opens and closes without a single line of JavaScript
- Respects accessibility best practices (keyboard navigation, ARIA, focus states)
- Looks modern and clean, ready to be themed for your brand

Step 1: The HTML Structure
We start with semantic HTML. Using the <nav> element tells assistive technologies that this block is a navigation region.
<nav class="navbar" aria-label="Main navigation">
<a href="#" class="navbar__logo">MyMirror</a>
<input type="checkbox" id="nav-toggle" class="nav-toggle" />
<label for="nav-toggle" class="nav-toggle-label" aria-label="Open menu">
<span></span>
</label>
<ul class="navbar__menu">
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#services">Services</a></li>
<li><a href="#blog">Blog</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
Why a Hidden Checkbox?
This is the classic checkbox hack. The hidden <input type="checkbox"> stores the state of the menu (open or closed). Clicking the <label> toggles the checkbox, and CSS reads that state to show or hide the menu. No JavaScript required.
Step 2: Base CSS Styling
Now let’s style the navigation bar for desktop first. We’ll handle mobile in the next step.
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: system-ui, -apple-system, sans-serif;
}
.navbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 2rem;
background-color: #1a1a2e;
color: #fff;
position: relative;
}
.navbar__logo {
font-size: 1.4rem;
font-weight: 700;
color: #fff;
text-decoration: none;
}
.navbar__menu {
list-style: none;
display: flex;
gap: 2rem;
}
.navbar__menu a {
color: #fff;
text-decoration: none;
font-weight: 500;
transition: color 0.2s ease;
}
.navbar__menu a:hover,
.navbar__menu a:focus {
color: #00b4d8;
outline: none;
}
/* Hide the checkbox and label on desktop */
.nav-toggle,
.nav-toggle-label {
display: none;
}

Step 3: Building the Hamburger Icon
The hamburger is built with a single <span> and two pseudo-elements (::before and ::after), which act as the three lines.
.nav-toggle-label {
display: none;
height: 100%;
cursor: pointer;
width: 30px;
}
.nav-toggle-label span,
.nav-toggle-label span::before,
.nav-toggle-label span::after {
display: block;
background: #fff;
height: 3px;
width: 100%;
border-radius: 2px;
position: relative;
transition: all 0.3s ease;
}
.nav-toggle-label span::before,
.nav-toggle-label span::after {
content: '';
position: absolute;
}
.nav-toggle-label span::before {
top: -8px;
}
.nav-toggle-label span::after {
top: 8px;
}
Step 4: Making It Responsive
Here’s where the magic happens. We use a media query to switch to mobile mode and the :checked pseudo-class to toggle the menu open or closed.
@media (max-width: 768px) {
.nav-toggle-label {
display: flex;
align-items: center;
}
.navbar__menu {
position: absolute;
top: 100%;
left: 0;
right: 0;
flex-direction: column;
background-color: #1a1a2e;
gap: 0;
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.navbar__menu li {
padding: 1rem 2rem;
border-top: 1px solid #2a2a3e;
}
/* Open menu when checkbox is checked */
.nav-toggle:checked ~ .navbar__menu {
max-height: 500px;
}
/* Animate hamburger into an X */
.nav-toggle:checked ~ .nav-toggle-label span {
background: transparent;
}
.nav-toggle:checked ~ .nav-toggle-label span::before {
transform: rotate(45deg);
top: 0;
}
.nav-toggle:checked ~ .nav-toggle-label span::after {
transform: rotate(-45deg);
top: 0;
}
}
How It Works (In Plain English)
- On screens narrower than 768px, the regular menu is hidden and the hamburger label appears.
- Clicking the hamburger toggles the hidden checkbox.
- When the checkbox is :checked, the menu slides open thanks to
max-height. - The same checked state animates the three lines into an X icon.

Step 5: Accessibility Improvements
A pretty menu that screen reader users can’t operate is a broken menu. Here are the essentials:
- Use semantic tags:
<nav>,<ul>, and<a>are recognized by assistive tech. - Visually hide the checkbox properly, but keep it focusable.
- Add visible focus styles so keyboard users see where they are.
- Use ARIA labels on the toggle and the nav region.
Add this to your CSS to hide the checkbox without removing it from the accessibility tree:
.nav-toggle {
position: absolute;
opacity: 0;
width: 1px;
height: 1px;
pointer-events: none;
}
.nav-toggle:focus ~ .nav-toggle-label {
outline: 2px solid #00b4d8;
outline-offset: 4px;
}
Browser Support in 2026
Every technique used here is fully supported in all modern browsers.
| Feature | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| Flexbox | Yes | Yes | Yes | Yes |
| :checked selector | Yes | Yes | Yes | Yes |
| CSS transitions | Yes | Yes | Yes | Yes |
| Container queries | Yes | Yes | Yes | Yes |

Bonus: Common Customizations
- Sticky header: add
position: sticky; top: 0; z-index: 100;to.navbar - Smooth scroll: add
html { scroll-behavior: smooth; } - Theme colors: replace hardcoded colors with CSS custom properties for easy theming
- Dropdown submenus: add nested
<ul>elements and use:hoveror:focus-withinto reveal them
Final Thoughts
Building a responsive navigation menu with CSS doesn’t have to be complicated. With around 80 lines of CSS and zero JavaScript, you have a fast, lightweight, and accessible navbar that works on every device.
This approach is great for landing pages, portfolios, and small business sites. For very large applications with deep menu trees or complex animations, JavaScript may eventually be the right tool, but for most websites, pure CSS is more than enough.
FAQ
How do I make a responsive navbar menu in CSS only?
Use a hidden checkbox combined with a label as a toggle. The :checked pseudo-class lets you change the state of the menu when the user taps the hamburger icon, without any JavaScript.
What is a responsive navigation bar?
A responsive navigation bar adapts its layout to the user’s screen size. It typically displays full menu items on desktop and collapses into a hamburger icon on mobile devices.
Is the checkbox hack good for accessibility?
It works well when implemented carefully. Keep the checkbox focusable (don’t use display: none), add proper ARIA labels, and ensure visible focus states for keyboard users.
Should I use JavaScript instead?
For simple sites, pure CSS is faster and more reliable. JavaScript becomes useful when you need advanced features like ARIA state syncing, complex dropdowns, or closing the menu when a user clicks outside.
Can I use this code in a WordPress theme?
Absolutely. Drop the HTML into your header.php file and place the CSS in your theme’s stylesheet. Adjust the class names if they conflict with existing styles.
