2.1 Animated burger menu: CSS morphing with advanced transitions
Creating an animated burger menu that seamlessly transforms is one of the most iconic micro-interactions in modern web design. This seemingly simple component actually harnesses the full power of CSS transformations and transitions to create a sleek and professional user experience.
Module: Module 1: Technical Fundamentals of the Web
Level : Beginner
Duration : 20 minutes
Prerequisites: None
Educational objectives
- Mastering the semantic HTML structure of a hamburger menu button
- Apply CSS transformations (rotate, translate, scale) precisely
- Create smooth transitions with custom cubic-bezier functions
- Manage absolute positioning and transformation points (transform-origin)
- Implement creative morphing variations (cross, arrow, offset effects)
Optimal HTML architecture
The structure of a burger menu must adhere to accessibility best practices while offering the necessary flexibility for animations. The element is essential because it naturally provides the interactive semantics expected by assistive technologies. Within this, we use three elements <span> which will represent the three horizontal bars of the hamburger menu.
ARIA attributes and accessibility
For an inclusive user experience, the button must include appropriate ARIA attributes. The attribute aria-label describes the action of the button, while’aria-expanded This indicates the menu's state (open/closed). This information is crucial for users navigating with screen readers. State management via JavaScript will allow these attributes to be dynamically updated during interactions.
Key point : The use of empty spans is acceptable here as they serve only for visual decoration, the meaning being carried by the parent button and its ARIA attributes.
Key points to remember
The HTML structure of a burger menu is based on a semantic button containing three spans, enriched with ARIA attributes for accessibility.
🎯 Mini-exercise: Accessible HTML structure
Create the HTML structure of a hamburger menu button with the appropriate accessibility attributes.
Reference code:
<button class="burger-menu" aria-label="Menu" aria-expanded="false"> <span class="burger-line burger-line--top"></span> <span class="burger-line burger-line--middle"></span> <span class="burger-line burger-line--bottom"></span> </button>
.burger-menu { width: 50px; height: 50px; background: transparent; border:none; cursor: pointer; padding: 12px; position: relative; border-radius: 8px; transition: background-color 0.3s ease; } .burger-menu:hover { background-color: rgba(0, 0, 0, 0.05); } .burger-line { display: block; width: 26px; height: 3px; background-color: #333; margin: 5px 0; border-radius: 2px; transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55); transform-origin: center; }
No JavaScript is required for this CSS exercise.
Monaco Editor v0.45
Mastery of the transform-origin
The transform origin determines the point around which rotations and scaling are performed. For a hamburger menu, this choice is crucial: rotating around the center of each bar will create a different effect than rotating around the center of the button. By default, transformations are performed around the center of the element (50% 50%), but you can specify precise positions in pixels, percentages, or keywords (top, bottom, left, right).
Relative vs. absolute positioning
The choice between relative and absolute positioning directly impacts the fluidity of animations. Relative positioning keeps the element within the normal document flow, while absolute positioning removes it from the flow. For complex animations where bars need to overlap or drastically change position, absolute positioning offers more control. However, this requires manually managing the spacing and alignment of elements.
Key point : Using transform-origin: center allows for symmetrical rotations, while left center or right center create more dynamic pivot effects.
Key points to remember
The transform-origin defines the pivot point of transformations, allowing for the creation of precise and natural rotation and scaling effects.
🎯 Mini-exercise: Animation with transform-origin
Create an animation where the burger bars rotate with different origin points to understand the impact of transform-origin.
Reference code:
<div class="transform-demo">
<div class="demo-section">
<h3>Origin: center (default)</h3>
<div class="burger-line demo-center"></div>
</div>
<div class="demo-section">
<h3>Origin: left center</h3>
<div class="burger-line demo-left"></div>
</div>
<div class="demo-section">
<h3>Origin: right center</h3>
<div class="burger-line demo-right"></div>
</div>
</div>
.transform-demo { padding: 20px; font-family: system-ui, sans-serif; } .demo-section { margin-bottom: 40px; text-align: center; } .demo-section h3 { margin-bottom: 20px; color: #333; font-size: 14px; } .burger-line { width: 40px; height: 4px; background: linear-gradient(45deg, #667eea, #764ba2); margin: 0 auto; border-radius: 2px; cursor: pointer; transition: transform 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); } .demo-center { transform-origin: center; } .demo-center:hover { transform: rotate(45deg) scale(1.2); } .demo-left { transform-origin: left center; } .demo-left:hover { transform: rotate(45deg) scale(1.2); } .demo-right { transform-origin: right center; } .demo-right:hover { transform: rotate(-45deg) scale(1.2); }
No JavaScript is required for this CSS exercise.
Monaco Editor v0.45
Advanced timing functions
Cubic-Bezier functions allow you to create custom transition curves that bring animations to life. Unlike predefined keywords (ease, linear, ease-in-out), cubic-Bezier offers complete control over acceleration and deceleration. The function takes four parameters (x1, y1, x2, y2) that define two control points of a cubic Bézier curve. Negative y-values or values greater than 1 create bouncing or elastic effects that are particularly appealing for micro-interactions.
Performance optimization
The use of the property will-change This tells the browser that an element will undergo transformations, allowing for GPU optimization. For hamburger menu animations, will-change: transform Significantly improves performance on mobile devices. However, be careful to remove this property once the animation is finished to avoid excessive memory consumption. The transform and opacity properties are the most efficient because they do not trigger a DOM reflow or repaint.
Key point : The cubic-bezier(0.68, -0.55, 0.265, 1.55) function creates an elegant bounce effect, perfect for burger menu interactions.
Key points to remember
Custom cubic-bezier curves transform basic transitions into engaging micro-interactions with natural bounce effects.
🎯 Mini-exercise: Comparing timing curves
Explore different cubic-bezier functions to understand their impact on the feel of the burger menu animation.
Reference code:
<div class="timing-comparison">
<div class="timing-demo">
<h3>ease (default)</h3>
<button class="burger-btn timing-ease">
<span></span>
<span></span>
<span></span>
</button>
</div>
<div class="timing-demo">
<h3>cubic-bezier bounce</h3>
<button class="burger-btn timing-bounce">
<span></span>
<span></span>
<span></span>
</button>
</div>
<div class="timing-demo">
<h3>cubic-bezier elastic</h3>
<button class="burger-btn timing-elastic">
<span></span>
<span></span>
<span></span>
</button>
</div>
</div>
.timing-comparison { display: flex; gap: 40px; justify-content: center; padding: 20px; flex-wrap:wrap; font-family: system-ui, sans-serif; } .timing-demo { text-align: center; } .timing-demo h3 { margin-bottom: 15px; font-size: 12px; color: #666; text-transform: uppercase; letter-spacing: 0.5px; } .burger-btn { width: 50px; height: 50px; background:white; border: 2px solid #ddd; border-radius: 12px; cursor: pointer; padding: 12px; will-change: transform; transition: border-color 0.2s ease; } .burger-btn:hover { border-color: #667eea; } .burger-btn span { display: block; width: 22px; height: 2px; background: #333; margin: 4px 0; border-radius: 1px; } .timing-ease span { transition: all 0.4s ease; } .timing-bounce span { transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); } .timing-elastic span { transition: all 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .burger-btn.active span:nth-child(1) { transform: rotate(45deg) translateY(6px); } .burger-btn.active span:nth-child(2) { opacity: 0; transform: scaleX(0); } .burger-btn.active span:nth-child(3) { transform: rotate(-45deg) translateY(-6px); }
No JavaScript is required for this CSS exercise.
Monaco Editor v0.45
Directional arrow morphing
Arrow morphing requires a different approach than the classic burger-to-X. The top and bottom bars must rotate and translate to form the arrow's branches, while the middle bar can either disappear or transform into a stem. The trick is to combine several transformations: rotation for the angle, translation for positioning, and scaling to adjust the length. This technique is particularly effective for navigation buttons (previous/next) or accordions.
Offset cross effect and temporal variations
The staggered cross animation adds further sophistication by introducing different delays for each bar. The property transition-delay This technique creates a cascading effect where the bars don't move simultaneously. It can be taken further with sequential transformations: the middle bar disappears first, followed by the outer bars which rotate with a slight time lag. The overall effect is more organic and more eye-catching.
Key point : Using transition-delay with stepped values (0s, 0.1s, 0.2s) creates an elegant and professional cascading effect.
Key points to remember
Creative morphing variants combine rotations, translations, and time delays to create unique and memorable micro-interactions.
🎯 Mini exercise: Multi-variation burger menu
Implement three morphing variants: classic cross, directional arrow and offset cross with temporal effects.
Reference code:
<div class="burger-variants">
<div class="variant-section">
<h3>Classic Cross</h3>
<button class="burger-menu variant-cross" onclick="this.classList.toggle('active')">
<span></span>
<span></span>
<span></span>
</button>
</div>
<div class="variant-section">
<h3>Arrow</h3>
<button class="burger-menu variant-arrow" onclick="this.classList.toggle('active')">
<span></span>
<span></span>
<span></span>
</button>
</div>
<div class="variant-section">
<h3>Offset Cross</h3>
<button class="burger-menu variant-stagger" onclick="this.classList.toggle('active')">
<span></span>
<span></span>
<span></span>
</button>
</div>
</div>
.burger-variants { display: flex; gap: 60px; justify-content: center; padding: 40px 20px; flex-wrap:wrap; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); font-family: system-ui, sans-serif; } .variant-section { text-align: center; } .variant-section h3 { color: white; margin-bottom: 20px; font-size: 14px; font-weight: 600; text-transform: uppercase; letter-spacing: 1px; } .burger-menu { width: 60px; height: 60px; background: rgba(255, 255, 255, 0.1); border: 2px solid rgba(255, 255, 255, 0.2); border-radius: 15px; cursor: pointer; backdrop-filter: blur(10px); transition: all 0.3s ease; } .burger-menu:hover { background: rgba(255, 255, 255, 0.15); border-color: rgba(255, 255, 255, 0.4); transform: translateY(-2px); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); } .burger-menu span { display: block; width: 26px; height: 3px; background:white; margin: 7px self; border-radius: 2px; transform-origin: center; } /* Classic cross */ .variant-cross span { transition: all 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55); } .variant-cross.active span:nth-child(1) { transform: rotate(45deg) translateY(10px); } .variant-cross.active span:nth-child(2) { opacity: 0; transform: scaleX(0); } .variant-cross.active span:nth-child(3) { transform: rotate(-45deg) translateY(-10px); } /* Arrow */ .variant-arrow span { transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .variant-arrow.active span:nth-child(1) { transform: rotate(-45deg) translateX(-8px) translateY(8px) scaleX(0.7); } .variant-arrow.active span:nth-child(2) { transform: translateX(-6px); } .variant-arrow.active span:nth-child(3) { transform: rotate(45deg) translateX(-8px) translateY(-8px) scaleX(0.7); } /* Shifted cross */ .variant-stagger span:nth-child(1) { transition: all 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55) 0s; } .variant-stagger span:nth-child(2) { transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55) 0.1s; } .variant-stagger span:nth-child(3) { transition: all 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55) 0.2s; } .variant-stagger.active span:nth-child(1) { transform: rotate(45deg) translateY(10px) translateX(2px); } .variant-stagger.active span:nth-child(2) { opacity: 0; transform: scaleX(0) rotate(180deg); } .variant-stagger.active span:nth-child(3) { transform: rotate(-45deg) translateY(-10px) translateX(2px); }
// Minimal JavaScript for toggles (included in the HTML onclick) // Alternative with addEventListener if preferred: /* document.querySelectorAll('.burger-menu').forEach(button => { button.addEventListener('click', () => { button.classList.toggle('active'); }); }); */
Monaco Editor v0.45
Summary
Key points to remember
- Semantic HTML structure A button with ARIA attributes containing three spans for accessibility
- Strategic transformation origin The pivot point determines the style and fluidity of the animation.
- Custom cubic-bezier functions Create professional rebound and elasticity effects
- GPU Optimization Will-change and transform/opacity properties for smooth performance
- Creative variations Time delays and combinations of transformations for unique effects
Sources
To deepen your knowledge:
Validate your knowledge
Test your understanding with this 10-question quiz:

