Pure CSSMIT licensed

Neumorphic Hamburger Toggle Button HTML CSS

Soft-UI physics done by the rules: the burger is a clay button raised from the surface by the canonical dual shadow (light from the top-left, dark to the bottom-right), and pressing it doesn't move it — it inverts the shadows to inset, so the button visibly sinks INTO the surface while its lines fold to an X. The state is a real checkbox (a toggle IS a checkbox), the soft menu card floats up on the same material system, and the docs carry the honest accessibility warning neumorphism demands.

Live Demo
Plain CSSTailwind v4
Try it

Build UI like this from scratch: Flexbox, Grid, animations & responsive CSS

ZTM · The CSS Bootcamp · 30‑day money-back guarantee · Affiliate link

10% off at checkout with code
Check ZTM Course

The code

<section class="chm-26" aria-label="Neumorphic hamburger toggle demo">
  <div class="chm-26__stage">
    <input type="checkbox" id="chm26-t" class="chm-26__state">
    <div class="chm-26__plate">
      <label for="chm26-t" class="chm-26__btn">
        <span class="chm-26__ico" aria-hidden="true"><i></i><i></i><i></i></span>
      </label>
      <span class="chm-26__word" aria-hidden="true"><b class="chm-26__w1">rest</b><b class="chm-26__w2">pressed</b></span>
    </div>
    <nav class="chm-26__menu" aria-label="Menu">
      <a href="#home" style="--i:0"><i class="chm-26__dot" aria-hidden="true"></i>Home</a>
      <a href="#library" style="--i:1"><i class="chm-26__dot" aria-hidden="true"></i>Library</a>
      <a href="#stats" style="--i:2"><i class="chm-26__dot" aria-hidden="true"></i>Statistics</a>
      <a href="#settings" style="--i:3"><i class="chm-26__dot" aria-hidden="true"></i>Settings</a>
    </nav>
    <p class="chm-26__note" aria-hidden="true">same clay, two shadows — pressed = the pair goes <code>inset</code>. Note the focus ring stays loud on purpose.</p>
  </div>
</section>
.chm-26,
.chm-26 *,
.chm-26 *::before,
.chm-26 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.chm-26 {
  background: var(--bg);
  width: 100%;
  min-height: 100vh;
  box-sizing: border-box;
  --clay: oklch(0.92 0.012 260);
  --lite: oklch(0.99 0.005 260);
  --shade: oklch(0.78 0.02 260);
  --ink: oklch(0.32 0.02 262);
  --mut: oklch(0.52 0.018 262);
  --acc: oklch(0.65 0.11 190);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
}

.chm-26__stage {
  position: relative;
  min-height: 480px;
  border-radius: 22px;
  background: var(--clay);
  padding: 34px 28px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 26px;
  border: 1px solid oklch(0.87 0.014 260);
}

.chm-26__state {
  position: absolute;
  width: 1px;
  height: 1px;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

.chm-26__plate {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

.chm-26__btn {
  width: 74px;
  height: 74px;
  display: grid;
  place-items: center;
  border-radius: 22px;
  background: var(--clay);
  cursor: pointer;
  box-shadow: -7px -7px 14px var(--lite),7px 7px 14px var(--shade);
  transition: box-shadow .3s ease;
}

.chm-26__btn:hover {
  box-shadow: -9px -9px 18px var(--lite),9px 9px 18px var(--shade);
}

.chm-26__state:focus-visible ~ .chm-26__plate .chm-26__btn {
  outline: 3px solid var(--acc);
  outline-offset: 3px;
}

.chm-26__state:checked ~ .chm-26__plate .chm-26__btn {
  box-shadow: inset -5px -5px 10px var(--lite),inset 5px 5px 10px var(--shade);
}

.chm-26__ico {
  position: relative;
  width: 24px;
  height: 16px;
  display: block;
}

.chm-26__ico i {
  position: absolute;
  left: 0;
  width: 24px;
  height: 2.6px;
  border-radius: 3px;
  background: var(--ink);
  transition: translate .3s cubic-bezier(.2,.7,.2,1),rotate .3s cubic-bezier(.2,.7,.2,1) .05s,opacity .2s,background .3s;
}

.chm-26__ico i:nth-child(1) {
  top: 0;
}

.chm-26__ico i:nth-child(2) {
  top: 6.7px;
}

.chm-26__ico i:nth-child(3) {
  top: 13.4px;
}

.chm-26__state:checked ~ .chm-26__plate .chm-26__ico i {
  background: var(--acc);
}

.chm-26__state:checked ~ .chm-26__plate .chm-26__ico i:nth-child(1) {
  translate: 0 6.7px;
  rotate: 45deg;
}

.chm-26__state:checked ~ .chm-26__plate .chm-26__ico i:nth-child(2) {
  opacity: 0;
  scale: .4 1;
}

.chm-26__state:checked ~ .chm-26__plate .chm-26__ico i:nth-child(3) {
  translate: 0 -6.7px;
  rotate: -45deg;
}

.chm-26__word {
  position: relative;
  height: 16px;
  overflow: hidden;
  display: block;
  font-size: 11px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--mut);
}

.chm-26__word b {
  display: block;
  height: 16px;
  line-height: 16px;
  font-weight: 700;
  transition: translate .35s cubic-bezier(.2,.7,.2,1);
}

.chm-26__state:checked ~ .chm-26__plate .chm-26__word b {
  translate: 0 -16px;
}

.chm-26__menu {
  width: min(280px,100%);
  display: grid;
  gap: 14px;
  padding: 6px;
}

.chm-26__menu a {
  display: flex;
  align-items: center;
  gap: 13px;
  min-height: 52px;
  padding: 0 20px;
  border-radius: 16px;
  background: var(--clay);
  color: var(--ink);
  font-size: 14px;
  font-weight: 700;
  text-decoration: none;
  box-shadow: -6px -6px 12px var(--lite),6px 6px 12px var(--shade);
  opacity: 0;
  translate: 0 12px;
  pointer-events: none;
  transition: box-shadow .25s,opacity .3s,translate .35s cubic-bezier(.2,.7,.2,1),color .2s;
}

.chm-26__state:checked ~ .chm-26__menu a {
  opacity: 1;
  translate: 0 0;
  pointer-events: auto;
  transition-delay: 0s,calc(.08s + var(--i)*.05s),calc(.08s + var(--i)*.05s),0s;
}

.chm-26__menu a:hover {
  color: var(--acc);
}

.chm-26__menu a:active {
  box-shadow: inset -4px -4px 8px var(--lite),inset 4px 4px 8px var(--shade);
}

.chm-26__menu a:focus-visible {
  outline: 3px solid var(--acc);
  outline-offset: 2px;
}

.chm-26__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--clay);
  box-shadow: inset -2px -2px 4px var(--lite),inset 2px 2px 4px var(--shade);
  transition: background .2s,box-shadow .2s;
}

.chm-26__menu a:hover .chm-26__dot,
.chm-26__menu a:focus-visible .chm-26__dot {
  background: var(--acc);
  box-shadow: none;
}

.chm-26__note {
  margin-top: auto;
  font-size: 12px;
  line-height: 1.6;
  color: var(--mut);
  text-align: center;
  max-width: 34ch;
  text-wrap: pretty;
}

.chm-26__note code {
  font: 11px ui-monospace,Menlo,Consolas,monospace;
  background: oklch(0.88 0.014 260);
  border-radius: 5px;
  padding: 1.5px 6px;
}

@media (prefers-reduced-motion: reduce) {
  .chm-26 * {
    transition-duration: .01s !important;
    transition-delay: 0s !important;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Hamburger Menu from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Neumorphic Hamburger Toggle Button HTML CSS
Source: https://codefronts.com/navigation/css-hamburger-menus/neumorphic-hamburger-menu-toggle-button/

Soft-UI physics done by the rules: the burger is a clay button raised from the surface by the canonical dual shadow (light from the top-left, dark to the bottom-right), and pressing it doesn't move it — it inverts the shadows to inset, so the button visibly sinks INTO the surface while its lines fold to an X. The state is a real checkbox (a toggle IS a checkbox), the soft menu card floats up on the same material system, and the docs carry the honest accessibility warning neumorphism demands.
## HTML
```html
<section class="chm-26" aria-label="Neumorphic hamburger toggle demo">
  <div class="chm-26__stage">
    <input type="checkbox" id="chm26-t" class="chm-26__state">
    <div class="chm-26__plate">
      <label for="chm26-t" class="chm-26__btn">
        <span class="chm-26__ico" aria-hidden="true"><i></i><i></i><i></i></span>
      </label>
      <span class="chm-26__word" aria-hidden="true"><b class="chm-26__w1">rest</b><b class="chm-26__w2">pressed</b></span>
    </div>
    <nav class="chm-26__menu" aria-label="Menu">
      <a href="#home" style="--i:0"><i class="chm-26__dot" aria-hidden="true"></i>Home</a>
      <a href="#library" style="--i:1"><i class="chm-26__dot" aria-hidden="true"></i>Library</a>
      <a href="#stats" style="--i:2"><i class="chm-26__dot" aria-hidden="true"></i>Statistics</a>
      <a href="#settings" style="--i:3"><i class="chm-26__dot" aria-hidden="true"></i>Settings</a>
    </nav>
    <p class="chm-26__note" aria-hidden="true">same clay, two shadows — pressed = the pair goes <code>inset</code>. Note the focus ring stays loud on purpose.</p>
  </div>
</section>
```
## CSS
```css
.chm-26,
.chm-26 *,
.chm-26 *::before,
.chm-26 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.chm-26 {
  background: var(--bg);
  width: 100%;
  min-height: 100vh;
  box-sizing: border-box;
  --clay: oklch(0.92 0.012 260);
  --lite: oklch(0.99 0.005 260);
  --shade: oklch(0.78 0.02 260);
  --ink: oklch(0.32 0.02 262);
  --mut: oklch(0.52 0.018 262);
  --acc: oklch(0.65 0.11 190);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
}

.chm-26__stage {
  position: relative;
  min-height: 480px;
  border-radius: 22px;
  background: var(--clay);
  padding: 34px 28px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 26px;
  border: 1px solid oklch(0.87 0.014 260);
}

.chm-26__state {
  position: absolute;
  width: 1px;
  height: 1px;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

.chm-26__plate {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

.chm-26__btn {
  width: 74px;
  height: 74px;
  display: grid;
  place-items: center;
  border-radius: 22px;
  background: var(--clay);
  cursor: pointer;
  box-shadow: -7px -7px 14px var(--lite),7px 7px 14px var(--shade);
  transition: box-shadow .3s ease;
}

.chm-26__btn:hover {
  box-shadow: -9px -9px 18px var(--lite),9px 9px 18px var(--shade);
}

.chm-26__state:focus-visible ~ .chm-26__plate .chm-26__btn {
  outline: 3px solid var(--acc);
  outline-offset: 3px;
}

.chm-26__state:checked ~ .chm-26__plate .chm-26__btn {
  box-shadow: inset -5px -5px 10px var(--lite),inset 5px 5px 10px var(--shade);
}

.chm-26__ico {
  position: relative;
  width: 24px;
  height: 16px;
  display: block;
}

.chm-26__ico i {
  position: absolute;
  left: 0;
  width: 24px;
  height: 2.6px;
  border-radius: 3px;
  background: var(--ink);
  transition: translate .3s cubic-bezier(.2,.7,.2,1),rotate .3s cubic-bezier(.2,.7,.2,1) .05s,opacity .2s,background .3s;
}

.chm-26__ico i:nth-child(1) {
  top: 0;
}

.chm-26__ico i:nth-child(2) {
  top: 6.7px;
}

.chm-26__ico i:nth-child(3) {
  top: 13.4px;
}

.chm-26__state:checked ~ .chm-26__plate .chm-26__ico i {
  background: var(--acc);
}

.chm-26__state:checked ~ .chm-26__plate .chm-26__ico i:nth-child(1) {
  translate: 0 6.7px;
  rotate: 45deg;
}

.chm-26__state:checked ~ .chm-26__plate .chm-26__ico i:nth-child(2) {
  opacity: 0;
  scale: .4 1;
}

.chm-26__state:checked ~ .chm-26__plate .chm-26__ico i:nth-child(3) {
  translate: 0 -6.7px;
  rotate: -45deg;
}

.chm-26__word {
  position: relative;
  height: 16px;
  overflow: hidden;
  display: block;
  font-size: 11px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--mut);
}

.chm-26__word b {
  display: block;
  height: 16px;
  line-height: 16px;
  font-weight: 700;
  transition: translate .35s cubic-bezier(.2,.7,.2,1);
}

.chm-26__state:checked ~ .chm-26__plate .chm-26__word b {
  translate: 0 -16px;
}

.chm-26__menu {
  width: min(280px,100%);
  display: grid;
  gap: 14px;
  padding: 6px;
}

.chm-26__menu a {
  display: flex;
  align-items: center;
  gap: 13px;
  min-height: 52px;
  padding: 0 20px;
  border-radius: 16px;
  background: var(--clay);
  color: var(--ink);
  font-size: 14px;
  font-weight: 700;
  text-decoration: none;
  box-shadow: -6px -6px 12px var(--lite),6px 6px 12px var(--shade);
  opacity: 0;
  translate: 0 12px;
  pointer-events: none;
  transition: box-shadow .25s,opacity .3s,translate .35s cubic-bezier(.2,.7,.2,1),color .2s;
}

.chm-26__state:checked ~ .chm-26__menu a {
  opacity: 1;
  translate: 0 0;
  pointer-events: auto;
  transition-delay: 0s,calc(.08s + var(--i)*.05s),calc(.08s + var(--i)*.05s),0s;
}

.chm-26__menu a:hover {
  color: var(--acc);
}

.chm-26__menu a:active {
  box-shadow: inset -4px -4px 8px var(--lite),inset 4px 4px 8px var(--shade);
}

.chm-26__menu a:focus-visible {
  outline: 3px solid var(--acc);
  outline-offset: 2px;
}

.chm-26__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--clay);
  box-shadow: inset -2px -2px 4px var(--lite),inset 2px 2px 4px var(--shade);
  transition: background .2s,box-shadow .2s;
}

.chm-26__menu a:hover .chm-26__dot,
.chm-26__menu a:focus-visible .chm-26__dot {
  background: var(--acc);
  box-shadow: none;
}

.chm-26__note {
  margin-top: auto;
  font-size: 12px;
  line-height: 1.6;
  color: var(--mut);
  text-align: center;
  max-width: 34ch;
  text-wrap: pretty;
}

.chm-26__note code {
  font: 11px ui-monospace,Menlo,Consolas,monospace;
  background: oklch(0.88 0.014 260);
  border-radius: 5px;
  padding: 1.5px 6px;
}

@media (prefers-reduced-motion: reduce) {
  .chm-26 * {
    transition-duration: .01s !important;
    transition-delay: 0s !important;
  }
}
```

How this works

Neumorphism is one material rule applied consistently: every element is the SAME color as the background, shaped purely by two opposing shadows from one imaginary light source:

.surface{ background:oklch(0.92 0.012 260); }
.btn{
  background:inherit;                       /* same clay        */
  box-shadow: -7px -7px 14px oklch(0.99 0.005 260),   /* light   */
               7px  7px 14px oklch(0.78 0.02 260);    /* shade   */
}
#chm26-t:checked ~ .btn{
  box-shadow: inset -5px -5px 10px oklch(0.99 0.005 260),
              inset  5px  5px 10px oklch(0.78 0.02 260);
}

Raised = outset pair; pressed = the SAME pair inset. That inversion is the entire interaction language — no translate, no scale; the light does the talking. Keep the light source fixed forever: one element lit from elsewhere shatters the illusion (the #1 neumorphic bug — copy-pasted shadows with flipped signs).

Radii follow the material too: soft clay wants generous rounding (18px on a 64px button), and the menu card raises with a LARGER blur (safer read: farther from surface = softer, wider shadows). Transition the box-shadow (both states declare full shadow pairs, so the interpolation is smooth).

The a11y warning, printed in bold because this style is famous for failing audits: neumorphism is intrinsically low-contrast — the button's edge against the surface is often under 1.4:1, far below the 3:1 WCAG 1.4.11 requires for control boundaries. This build compensates where it counts: ink-dark lines (7.8:1), a real focus ring that ignores the soft aesthetic entirely, and the pressed state ALSO changes line color (never shadow-only state signaling). Treat soft-UI as a skin over compliant bones, not a substitute for them.

Make it yours

  • The whole theme is three tokens: --clay (surface), --lite, --shade. Warm clay: hue 60; cool porcelain: hue 260 (as shipped). Keep lightness within ±0.10 of the surface or shadows go muddy.
  • Depth: shadow offset 7px/blur 14px = standard; 4/8 = subtle plaque; 10/24 = pillow. Offsets and blur scale together, roughly 1:2.
  • The menu card's items are raised pills that flatten (shadow:none) on press — the same inversion grammar; restyle only the shadow recipe to retheme everything.
  • Add an accent (this demo's teal check-dot) sparingly — pure monochrome neumorphism reads as decoration, not UI; one saturated cue restores hierarchy.

Gotchas — read before shipping

  • Neumorphism only exists on midtone surfaces — on white the light shadow vanishes, on near-black the dark one does. If your brand background is either, this is the wrong pattern (and this demo's docs say so out loud).
  • background:inherit on the button assumes it sits DIRECTLY on the surface — inside a differently-colored card the inherit chain breaks; re-declare the clay explicitly.
  • Soft shadows are paint-heavy; a page of 50 neumorphic elements measurably slows low-end scroll. It's a hero-element style, not a system.
  • Never remove the focus outline because 'it ruins the softness' — that instinct is how neumorphic UIs fail WCAG 2.4.7. Ring it anyway (this demo's ring offsets 3px so the clay stays clean at rest).

Browser support

ChromeSafariFirefoxEdge
114+17.5+121+114+

Floor set by oklch(), text-wrap as this demo is written. The core technique itself goes back further — Chrome 111+.

Dual box-shadows and inset transitions run in every engine of the last decade — the floor is only the oklch() palette. This is also the easiest demo to port to hex for legacy.

Search CodeFronts

Loading…