Navigation✨ Premium

Morphing Hamburger

Three CSS lines pivot into an X with back-ease spring — the most recognized open/closed state indicator in mobile UI.

CSSMorph

You'll get access to the interactive demo with a free account.

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Navigation has 36 effects, including 4 free. Explore the category →

3 usage examples

Mobile header — Main app navigation — Morphing Hamburger example 1

① Mobile header — Main app navigation

WhenThe user visits the app on mobile and needs to access the main menu hidden behind the hamburger button.
WhyMorphing to an X clearly communicates that tapping again will close the menu — better state feedback than any static icon.
SettingsWhite 24×2 px lines on a dark header, click target 44×44 px for touch comfort, 0.35 s transition — slightly faster than the default for a responsive mobile feel.
Dashboard — Sidebar panel toggle button — Morphing Hamburger example 2

② Dashboard — Sidebar panel toggle button

WhenOn a dashboard with a collapsible sidebar, the user wants to hide or reveal the navigation panel to free up workspace.
WhyThe hamburger is immediately recognisable as a panel toggle; the X confirms the sidebar is open and can be closed with another click.
SettingsLight-grey #94a3b8 lines, 20×2 px, 32×32 px container, 0.25 s transition for a more responsive desktop feel.
Filter panel — Catalogue settings drawer trigger — Morphing Hamburger example 3

③ Filter panel — Catalogue settings drawer trigger

WhenOn a results page or catalogue, the user opens a side filter panel via the hamburger button in the toolbar.
WhyMorphing to X signals the panel is active and can be dismissed with another click — the expected, consistent drawer pattern.
SettingsAccent-colour lines (#6366f1), 22×2.5 px, to stand out from the neutral toolbar; back-ease preserved for the same recognisable spring feel.

How it works

The effect uses three <span class="line"> elements positioned absolutely inside a 36×36 px flexbox container. In hamburger state, lines 1 and 3 are offset vertically via translateY(±8px); line 2 is deliberately shorter (18 px vs 24 px) — a subtle asymmetry that gives the resting icon its character.

The transformation is entirely CSS-driven: when the .morphed class is added to the container, lines 1 and 3 return to Y=0 and rotate(±45deg) to form an X, while line 2 disappears via width: 0 and opacity: 0. No JS is involved in the rendering.

The cubic-bezier(.68, -.55, .27, 1.55) easing is a back ease-out: the lines slightly overshoot their target angle before settling back to the final position. This mechanical spring — invisible with a linear transition — reinforces the perception of weight and response without any JS. The 0.4 s duration lets the eye follow the motion without perceived lag.

JavaScript is limited to a single classList.toggle('morphed') triggered on click. If the user clicks before the transition ends, the browser resumes the animation from the current intermediate state and reverses it — native browser behavior, no manual state management required.

Accessibility

  • prefers-reduced-motion absent: no @media (prefers-reduced-motion: reduce) rule is present in the code. The transition always runs regardless of OS settings. Add manually if needed: .morphing-hamburger .line { transition: none; } inside the media query.
  • The trigger element is a <div> with a JS handler, not a native <button> — not keyboard-focusable by default. Add tabindex="0" and a keydown handler (Enter/Space) for WCAG 2.1 compliance.
  • No aria-expanded present: screen readers cannot tell whether the menu is open or closed. Toggle aria-expanded="false" / "true" on click in the handler.
  • No aria-label: the control has no accessible name. Add aria-label="Open menu" updated dynamically on each toggle.
  • Line contrast (#fff on #0a0a0f): ratio > 21:1 — WCAG AA and AAA compliant unconditionally.

Browser compatibility

Requires CSS Transitions and classList.toggle — available in every modern browser since 2014.

Chrome 57+✓ Full
Firefox 36+✓ Full
Safari 9+✓ Full
Edge 16+✓ Full
Mobile iOS✓ Touch OK
Android Chrome✓ Full

Without JavaScript, the icon stays static in hamburger state — no broken styling, no errors. Without CSS transition support (IE9 and older), the hamburger-to-X change happens instantly with no animation.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="morphing-hamburger" id="morphingHamburger">
  <span class="line"></span>
  <span class="line"></span>
  <span class="line"></span>
</div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
background (on .morphing-hamburger .line) #fff Line color. Change to a brand color or use currentColor to inherit from the parent text color.
width (on .line) 24px Width of lines 1 and 3. Adjust line 2 (.line:nth-child(2), 18 px) in parallel to maintain the asymmetric ratio — e.g. 20 px / 15 px for a compact icon.
height (on .line) 2px Line thickness. 3 px for a bold look, 1 px for ultra-thin.
transition (on .line) .4s cubic-bezier(.68,-.55,.27,1.55) Animation duration and curve. Reduce to .2s for a snappier response, or swap the curve for ease-in-out to remove the overshoot and get a clean transition.
translateY (lines 1 and 3) ±8px Vertical gap between lines in hamburger state. Reduce to ±6 px for a compact icon, increase to ±10 px for a more open look.
width / height (on .morphing-hamburger) 36px / 36px Container and click-target size. Set to at least 44 px to meet WCAG touch target size recommendations (2.75 rem).
border-radius (on .line) 2px End-cap rounding. 0 for sharp square ends, 4 px for a softer rounded look.

FAQ

Yes — replace <div class="morphing-hamburger" id="morphingHamburger"> with a <button> carrying the same class and id. Add background: none; border: none; padding: 0; cursor: pointer; in CSS to neutralise browser default styles. The JS toggle is unchanged; update aria-label and aria-expanded on click for WCAG compliance.
The classList.toggle('morphed') manages the visual state of the icon. In the same handler, trigger your menu: nav.classList.toggle('open'), a height transition, or a transform: translateX. The icon and menu share the same toggle and stay in sync at all times with no extra state.
No — CSS transitions resume from their current intermediate state. If the user clicks before the transition ends, the browser reverses the animation from the lines' current position. No manual state management is needed, and the visual result stays coherent.