CSS Hamburger Menu Generator
Five animations. CSS-only, or a button with aria-expanded.
Click the icon. Like cross, but the middle bar collapses horizontally instead of fading — reads as being squeezed out.
/* CSS-only toggle: a visually hidden checkbox holds the state,
and the sibling selector styles the button from it. No JavaScript. */
.hamburger__checkbox {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.hamburger {
display: grid;
gap: 6px;
padding: 8px;
border: 0;
background: none;
cursor: pointer;
}
.hamburger span {
display: block;
width: 26px;
height: 3px;
border-radius: 2px;
background: #18181b;
transition: transform 0.3s ease, opacity 0.3s ease, background 0.3s ease;
}
.hamburger__checkbox:checked + .hamburger span {
background: #2563eb;
}
.hamburger__checkbox:checked + .hamburger span:nth-child(1) {
transform: translateY(9px) rotate(45deg);
}
.hamburger__checkbox:checked + .hamburger span:nth-child(2) {
transform: scaleX(0);
}
.hamburger__checkbox:checked + .hamburger span:nth-child(3) {
transform: translateY(-9px) rotate(-45deg);
}
@media (prefers-reduced-motion: reduce) {
.hamburger span { transition: none; }
}
Three spans and a transform
A hamburger icon is three spans and two rotations, and the whole thing hinges on one number: how far the outer bars travel to meet in the middle. Get it wrong by a pixel and the X is visibly off-centre. That distance is the gap plus the bar thickness, and it is recalculated here every time you move a slider.
How to use it
-
Choose an animation
Squeeze, cross, arrow, spin or minus. Click the icon in the preview to play it both ways.
-
Choose a toggle method
CSS-only with the checkbox hack, or a real button with aria-expanded and four lines of JavaScript.
-
Shape the bars
Width, thickness, gap, radius and speed. The open-state transforms are recalculated from the gap so the strokes always cross dead centre.
-
Copy it
The CSS with the open-state rules and a reduced-motion fallback, plus HTML with a proper accessible name.
CSS-only or accessible — pick knowingly
The checkbox hack genuinely needs no JavaScript: a hidden checkbox holds the state and a
sibling selector styles the label. The cost is that assistive technology announces a checkbox,
not a disclosure button, so the user hears "checked" rather than "expanded". A real
<button> with aria-expanded is four lines of JavaScript and
reads correctly. Both are here; neither is silently better.
The icon must change state
An icon that looks identical open and closed gives no feedback that the tap did anything. The
X or arrow is the visual counterpart of aria-expanded="true" — they should always
flip together.
Give it a name
Three empty spans have no accessible name, so a screen reader announces only "button". Add
aria-label="Toggle navigation" or visually hidden text. The generated HTML
includes it.
What it opens
The icon is the easy half; the panel it reveals is a layout problem. For an off-canvas drawer or a collapsing sidebar, see the dashboard layout or the sidebar generator.
Frequently asked questions
How do I make a CSS-only hamburger menu?
<input type="checkbox"> holds the open state, a <label> holding three spans is the icon, and :checked + .hamburger span transforms the bars. No JavaScript at all.Is the checkbox hack accessible?
<button> with aria-expanded and four lines of JavaScript. This tool emits either.How do I animate a hamburger into an X?
What is the difference between the cross and squeeze animations?
opacity: 0; squeeze collapses it horizontally with transform: scaleX(0), which reads as the bar being pushed out of the way rather than disappearing.What accessible name should a hamburger button have?
aria-label="Toggle navigation" or visually hidden text inside the button. An icon made of three empty spans has no accessible name at all, so a screen reader announces only "button".Should the hamburger icon change when the menu is open?
aria-expanded="true", and both should change together.