cssgridgen

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; }
}
Free tool

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

  1. Choose an animation

    Squeeze, cross, arrow, spin or minus. Click the icon in the preview to play it both ways.

  2. Choose a toggle method

    CSS-only with the checkbox hack, or a real button with aria-expanded and four lines of JavaScript.

  3. 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.

  4. 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?
Use the checkbox hack: a visually hidden <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?
Partly. It is keyboard operable, but a screen reader announces a checkbox rather than a disclosure button — so the control reads as "checked" rather than "expanded". If that matters, use a real <button> with aria-expanded and four lines of JavaScript. This tool emits either.
How do I animate a hamburger into an X?
Translate the top and bottom bars to the vertical centre, rotate them 45 and −45 degrees, and hide the middle one. The translate distance is the gap plus the bar thickness — get that wrong and the two strokes cross slightly off-centre.
What is the difference between the cross and squeeze animations?
Only the middle bar. Cross fades it out with 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?
Yes — an icon that stays identical gives no feedback that anything happened. The X or arrow state is the visual counterpart of aria-expanded="true", and both should change together.