Résidence Les Tilleuls · Building B
Flat 12Mr and Mrs Lebrun · 3rd floor
Press Call to ring the flat
Two 180 × 54 px buttons cut from the same material as their #e0e5ec background: a dark shadow bottom right, a white shadow top left, and the pressed state switches to inset shadows. Pure CSS, zero JavaScript.
Updated
This effect is free — the Glassmorphism category contains 47 effects total, including 6 free. Effect.Labs has 840 vanilla effects. Explore the category →
Résidence Les Tilleuls · Building B
Flat 12Mr and Mrs Lebrun · 3rd floor
Press Call to ring the flat
Espresso
Disarmed
Doors and windows closed. Ready to arm.
4 zones · 2 detectors · garage included
It all fits in three rules. .neu-btn is 180 × 54 px, border-radius: 16px, no border, with the same background as the surface (#e0e5ec) and two opposite drop shadows: 8px 8px 16px #b8bec7 (the shaded side) and -8px -8px 16px #ffffff (the lit side). That pair is what lifts the button out of the material: no outline, no gradient. On hover the shadows tighten to 6 px / 12 px and the text turns from #6b7280 to violet #8b5cf6; transition: all 0.2s ease smooths the change. :active and the .neu-btn--pressed class share one rule: both shadows become inset (inset 4px 4px 8px #b8bec7, inset -4px -4px 8px #ffffff) and the button looks carved in. The SVG icon (18 px, 2 px stroke) follows currentColor, so it turns violet with the text.
Neumorphism only works when the light shadow falls on the button's own tint: on a black background, -8px -8px 16px #ffffff becomes a white glow; on a white one it vanishes. That is why .neu-btn-scene declares its own surface (background: #e0e5ec, padding: 32px, border-radius: 24px). The 32 px hold the whole shadow (8 px offset + 16 px blur = 24 px), so the effect keeps its relief when pasted into any page, light or dark, without touching body. Measured in Chromium and Firefox: computed background rgb(224, 229, 236) on the container, no glow outside the surface.
The .neu-btn--pressed class is a state you set yourself: the CSS never changes it on its own. For a button that stays down (call in progress, alarm armed), add it on click and pair it with aria-pressed="true" — which is what the three scenes below do with a dozen lines of JavaScript that are not part of the effect.
#6b7280 on #e0e5ec measures 3.8:1 and the violet #8b5cf6 (hover, pressed) 3.3:1, for 15.2 px text at weight 600 — below the 4.5:1 threshold in both cases. Darken the label to #4b5563 (6.0:1) and the violet to #6d28d9 (5.6:1): the relief does not depend on them.#b8bec7 on #e0e5ec is 1.5:1, far from the 3:1 required for components (WCAG 1.4.11), and the white shadow has no contrast at all. A low-vision user may only see floating text: add a 1px solid #9ca3af border if your audience needs it, or keep the icon and text clearly readable..neu-btn--pressed is purely visual. A screen reader announces “button” without saying whether it is pressed: set aria-pressed="true" / "false" together with the class. The information is also carried by color alone (violet) and the shadow: a status text next to it (“Calling…”) removes any doubt.<button> elements, reachable with Tab and activated with Enter or Space. The code defines no focus style: the browser's default ring shows (checked in Chromium and Firefox). To match the violet: .neu-btn:focus-visible { outline: 2px solid #8b5cf6; outline-offset: 3px }. Add type="button" if the button lives in a form, otherwise it submits it.prefers-reduced-motion has nothing to switch off.CSS only: multiple and inset box-shadow, border-radius, transition, flexbox with gap (Chrome 84, Firefox 63, Safari 14.1), inline SVG with stroke: currentColor. Zero JavaScript, zero dependencies. Identical rendering measured in Chromium, Firefox and WebKit (Playwright): surface background rgb(224, 229, 236), inset shadows on the pressed state. The 'Inter' font is not loaded by the effect: without it, the browser uses the system sans-serif.
Without box-shadow (very old browsers), the buttons stay rounded gray rectangles, readable and clickable, with no relief. Before flex-container gap, the two buttons touch and the icon sticks to the text: add margins. On touch screens there is no hover: :active only lasts while the finger is down, hence the .neu-btn--pressed class for a state that persists.
Copy the three blocks into your page. No dependencies.
<div class="neu-btn-scene">
<button class="neu-btn">
<svg viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
Continue
</button>
<button class="neu-btn neu-btn--pressed">
<svg viewBox="0 0 24 24"><path d="M20 6L9 17l-5-5"/></svg>
Done
</button>
</div>
.neu-btn-scene {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
background: #e0e5ec;
padding: 32px;
border-radius: 24px;
}
.neu-btn {
width: 180px;
height: 54px;
border-radius: 16px;
background: #e0e5ec;
box-shadow: 8px 8px 16px #b8bec7, -8px -8px 16px #ffffff;
border: none;
font-family: 'Inter', sans-serif;
font-size: 0.95rem;
font-weight: 600;
color: #6b7280;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.neu-btn:hover {
box-shadow: 6px 6px 12px #b8bec7, -6px -6px 12px #ffffff;
color: #8b5cf6;
}
.neu-btn:active,
.neu-btn--pressed {
box-shadow: inset 4px 4px 8px #b8bec7, inset -4px -4px 8px #ffffff;
color: #8b5cf6;
}
.neu-btn svg {
width: 18px;
height: 18px;
stroke: currentColor;
fill: none;
stroke-width: 2;
}
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Material tint (CSS — #e0e5ec on .neu-btn and .neu-btn-scene) |
#e0e5ec, shadows #b8bec7 and #ffffff | Change the three colors together: the background, a shadow about 15% darker and a lighter one. For a dark material: background #2a2d34, shadows #1d1f24 and #373b44. The button and its surface must always share the same tint, otherwise the relief reads as a smudge. |
Depth (CSS — 8px 8px 16px at rest, 6px 6px 12px on hover) |
8 px offset, 16 px blur | Double offset and blur (12 px / 24 px) for stronger relief, and raise the padding of .neu-btn-scene to match (it must stay above offset + blur). Below 4 px / 8 px, the button melts into the surface. |
Pressed state (CSS — .neu-btn--pressed class) |
inset 4px 4px 8px | Set the class from JavaScript for a state that persists, together with aria-pressed. For a deeper hollow: inset 6px 6px 12px #b8bec7, inset -6px -6px 12px #ffffff. |
Size (CSS — width: 180px; height: 54px; border-radius: 16px) |
180 × 54 px | A square appliance key (64 × 64 px, border-radius: 50%) keeps the same look. Keep at least 44 px of height for touch. |
Accent color (CSS — color: #8b5cf6 on hover and pressed) |
#8b5cf6 (3.3:1 on the material) | #6d28d9 reaches 5.6:1; an appliance green #166534 5.6:1. The icon follows, since it uses currentColor. |
Because the light shadow -8px -8px 16px #ffffff is meant to fall on the button's own tint. The sold code therefore gives .neu-btn-scene its own #e0e5ec surface with 32 px of padding: keep that container around the buttons and the relief stays right on any background. If you remove the surface, place the buttons on a block of their own color.
:active only lasts while pressing. Add the class from JavaScript: btn.addEventListener('click', () => { const on = btn.classList.toggle('neu-btn--pressed'); btn.setAttribute('aria-pressed', on); }). For a group where only one button stays down (Start / Pause), remove the class from the others first.
Neu Button is an action: you press, it sinks in, the page decides what happens next. Neu Toggle is a two-state switch with a carved track and a knob that slides with a bounce, announced as role="switch". To turn a setting on or off, use the toggle; to start or stop something, the button.