Chapter 4: Menu Icon
How to create a Menu Icon (also called hamburger menu icon, navigation toggle icon, or mobile menu button).
This is the famous three-line icon ≡ that almost every mobile website and app uses to open a side menu or navigation drawer.
We will build it from scratch in several common ways so you understand the concept deeply and can choose what fits your project best.
What we want to achieve (visual goal)
- Three horizontal lines
- Looks clean and modern
- Changes into an X (close icon) when clicked
- Smooth animation
- Works on mobile and desktop
- Can be done with pure CSS or very little JavaScript
Let’s start with the most common and cleanest approaches.
Method 1 – Pure CSS Hamburger → X (most popular 2024–2026 style)
Step 1 – HTML structure
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <title>Menu Icon</title> <link rel="stylesheet" href="style.css"> </head> <body> <!-- The menu button --> <button class="menu-toggle" aria-label="Toggle menu"> <span class="hamburger"> <span class="bar"></span> <span class="bar"></span> <span class="bar"></span> </span> </button> <!-- Just for demo --> <p>Click the icon ↑</p> </body> </html> |
Important notes about HTML:
- We use <button> (not <div>) → better for accessibility
- aria-label → screen readers understand what it does
- Three <span> elements → these become the three lines
Step 2 – CSS (very detailed)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
/* Reset & page setup */ * { margin: 0; padding: 0; box-sizing: border-box; } body { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: #f0f2f5; font-family: system-ui, sans-serif; } /* The button itself */ .menu-toggle { background: none; border: none; cursor: pointer; padding: 12px; position: relative; } /* Container for the three bars */ .hamburger { display: inline-block; position: relative; width: 32px; height: 24px; } /* Each bar */ .bar { position: absolute; width: 100%; height: 3.5px; background: #222; border-radius: 2px; left: 0; transition: all 0.35s ease; } /* Positions of the three bars when closed */ .bar:nth-child(1) { top: 0; } .bar:nth-child(2) { top: 10px; } .bar:nth-child(3) { top: 20px; } /* ── When menu is OPEN ── */ .menu-toggle.active .bar:nth-child(1) { transform: translateY(10px) rotate(45deg); } .menu-toggle.active .bar:nth-child(2) { opacity: 0; transform: translateX(-20px); /* small slide helps the animation feel nicer */ } .menu-toggle.active .bar:nth-child(3) { transform: translateY(-10px) rotate(-45deg); } /* Optional: hover effect */ .menu-toggle:hover .bar { background: #0066ff; } |
Step 3 – Tiny bit of JavaScript (to toggle the class)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
<script> const toggle = document.querySelector('.menu-toggle'); toggle.addEventListener('click', () => { toggle.classList.toggle('active'); }); </script> |
Result: You now have a very clean hamburger icon that smoothly turns into an X when clicked.
Method 2 – Using only one element (very modern & minimal)
Many designers now prefer this version because it has fewer DOM elements.
|
0 1 2 3 4 5 6 7 8 |
<button class="menu-btn" aria-label="Toggle menu"> <span></span> </button> |
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
.menu-btn { position: relative; width: 44px; height: 44px; background: none; border: none; cursor: pointer; } .menu-btn span, .menu-btn span::before, .menu-btn span::after { content: ""; position: absolute; width: 28px; height: 3.5px; background: #222; border-radius: 2px; left: 8px; transition: all 0.4s cubic-bezier(0.65, -0.2, 0.35, 1.1); } .menu-btn span { top: 20px; } .menu-btn span::before { top: -9px; } .menu-btn span::after { top: 9px; } /* Open state */ .menu-btn.active span { background: transparent; } .menu-btn.active span::before { transform: translateY(9px) rotate(45deg); } .menu-btn.active span::after { transform: translateY(-9px) rotate(-45deg); } |
Same tiny JavaScript:
|
0 1 2 3 4 5 6 7 8 |
document.querySelector('.menu-btn').addEventListener('click', function() { this.classList.toggle('active'); }); |
This version is cleaner in HTML and very popular in 2025–2026 minimalist designs.
Method 3 – Animated with scale + rotation (fancy version)
This one feels more playful — good for creative websites.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
.menu-toggle.fancy .hamburger { transition: transform 0.4s ease; } .menu-toggle.fancy.active .hamburger { transform: rotate(90deg); } .menu-toggle.fancy .bar { transition: all 0.35s ease; } .menu-toggle.fancy.active .bar:nth-child(1) { transform: translateY(10px) rotate(45deg); width: 60%; left: 20%; } .menu-toggle.fancy.active .bar:nth-child(3) { transform: translateY(-10px) rotate(-45deg); width: 60%; left: 20%; } |
Just add class=”menu-toggle fancy” and it gives a nice twist effect.
Quick checklist – things good teachers always remind students
- Always use <button> (not div) → accessibility
- Always add aria-label=”Open menu” or aria-expanded=”false/true”
- Use transition with cubic-bezier for smoother feel
- Make sure there is enough padding around the icon (44×44 px minimum for touch targets)
- Test on mobile — tap area must feel good
- When the menu opens → change aria-label to “Close menu”
Example with aria-expanded:
|
0 1 2 3 4 5 6 7 8 |
<button class="menu-toggle" aria-label="Open main menu" aria-expanded="false"> ... </button> |
|
0 1 2 3 4 5 6 7 8 9 10 11 |
toggle.addEventListener('click', () => { const isExpanded = toggle.getAttribute('aria-expanded') === 'true'; toggle.setAttribute('aria-expanded', !isExpanded); toggle.setAttribute('aria-label', isExpanded ? 'Open main menu' : 'Close main menu'); toggle.classList.toggle('active'); }); |
Variations you should know
- Thicker bars (bold style) → height: 4.5–5px
- Rounded bars → border-radius: 4px
- Color change on active → .active .bar { background: #e63946; }
- Scale effect on hover → transform: scale(1.1);
- Animated dots version (like LinkedIn mobile) → three dots → X
- Only icon changes, no text → common on mobile
- Icon + word “Menu” → better for some desktop layouts
Your next practice tasks
- Make the X red when active
- Add a very subtle scale animation when clicking
- Make the bars shorter when open (like many apps do)
- Create a version that becomes an arrow instead of X
- Combine it with an actual side menu that slides in
Would you like me to explain any of these next steps in detail?
Examples of things I can show you:
- How to make the side menu slide from left when clicking
- How to do it with only CSS (no JavaScript at all)
- How to make it with SVG instead of spans
- How to animate it with Framer Motion (React)
- How to make it look like popular apps (Instagram, YouTube, Spotify…)
- How to make it accessible for keyboard & screen readers
- Common beginner mistakes and how to avoid them
Just tell me which direction you want to go next — I’ll explain it slowly and with complete examples. 😊
