Glass Morphing Button
A glass button that changes shape on hover, in pure CSS: side padding 36 → 52 px, corners 14 → 20 px, violet border and inner glow, 5% zoom — it goes from 161 × 46 to 203 × 53 px on screen and stretches toward the hand, over two colored glows floating behind it.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Glassmorphism has 47 effects, including 6 free. Explore the category →
3 usage examples



How it works
A real <button class="glass-morph-btn"> placed in a small 300 × 180 px .glass-btn-scene. Behind it, two glows — a 100 px violet #8b5cf6 top left, an 80 px cyan #06b6d4 bottom right — blurred at 40 px and animated by glassBtnOrb (5 s, the second one reversed): since the keyframes are symmetrical, reversing gives the same path: they slide together 20 px right and 15 px up, then come back. The button is 8% white with backdrop-filter: blur(20px), a 15% white border, 14 px corners, a white uppercase label (14.4 px, weight 600, 1 px letter spacing), a black 0 4px 16px shadow and, in ::before, a 135° highlight from 15% white to transparent. The dark #0a0a0f background comes from the .demo-preview frame shipped with the code: the scene itself is transparent.
On hover, everything changes at once over 0.4 s (transition: .4s cubic-bezier(.4, 0, .2, 1), applied to all properties): padding 14px 36px → 16px 52px, corners 14 → 20 px, background at 12%, violet border at 40%, violet 0 8px 32px shadow plus an inset 0 0 20px inner glow, and scale(1.05). Measured in Chromium: 161 × 46 px at rest, a 193 × 50 px box on hover, i.e. 203 × 53 px on screen with the zoom; in Firefox, 157 × 48 → a 189 × 52 px box, 198 × 55 px on screen (the label renders slightly narrower there). On click, :active drops to scale(.98). Since it is the padding that grows, the button really takes up space: centered, it stretches both ways; left-aligned, it grows to the right and pushes whatever follows it on the line.
The blur is declared twice, backdrop-filter: blur(20px) and -webkit-backdrop-filter: blur(20px), and it is not a repeat: Safari before version 18 (including iOS 17 and earlier) only reads the prefixed form, and without it would show a merely translucent button, with no frosting. Otherwise the button depends on nothing: no JavaScript, no dependency, no site variable.
Accessibility
- Keyboard: it is a
<button>, it takes focus with Tab and activates with Enter or Space. The stretch, however, only exists on hover: with the keyboard the button keeps its resting size with the browser's default focus ring. Add.glass-morph-btn:focus-visibleto the:hoverrule so focus produces the same shape, and keep a visible outline (outline: 2px solid #a78bfa; outline-offset: 3px). - Touch: on mobile there is no hover before the finger. On the first tap most mobile browsers apply
:hoverand leave it in place after release, so the button stays stretched until you touch elsewhere. To limit the stretch to pointer devices, wrap the rule in@media (hover: hover) { … }. - No prefers-reduced-motion in the code: the glows keep floating and the 0.4 s transition stays active under emulation. The stretch is user-triggered and short; add
@media (prefers-reduced-motion: reduce) { .glass-morph-btn { transition: none } .glass-btn-bg-1, .glass-btn-bg-2 { animation: none } }for a state change without motion. - Contrast: white label measured between 6.5:1 and 10:1 on the button depending on the glow passing behind. On a light photo, 8% white is no longer enough: the real-estate scene switches the button background to 22% dark brown. The label is uppercased by CSS (
text-transform): write it normally in the HTML and screen readers will read it as a word, not letter by letter.
Browser compatibility
CSS only: backdrop-filter and -webkit-backdrop-filter, filter: blur(), transition on padding, border-radius, box-shadow (including inset) and transform, @keyframes. Zero JavaScript, zero dependency. Backdrop blur: Chrome 76+, Edge 79+, Firefox 103+, Safari 9+ with the prefix (declared in the code), unprefixed since Safari 18. Measured in Chromium 151 and Firefox 153: same computed values at rest and on hover (padding, corners, violet border, scale(1.05)). Firefox and WebKit 26.5 under Playwright recognize the blur (blur(20px) computed) but their automated screenshots do not paint it, even on a minimal test: the frosting was only seen in Chromium.
Without backdrop blur, the button stays an 8% white veil over the glows, and the stretch works the same: transitions on padding, border-radius and box-shadow exist in every current browser. Over a sharp photo, plan a more opaque background inside @supports not (backdrop-filter: blur(1px)).
The code
HTML structure to paste into your page (the CSS + JS come with Premium):
<div class="demo-preview">
<div class="glass-btn-scene">
<div class="glass-btn-bg-1"></div>
<div class="glass-btn-bg-2"></div>
<button class="glass-morph-btn">…</button>
</div>
</div>
The full HTML + CSS + JS of Glass Morphing Button, ready to paste — plus all 840 effects. Or $299 once, lifetime access.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Stretch (CSS — padding: 14px 36px → :hover { padding: 16px 52px }) |
+16 px on each side, +2 px in height | The gap between the two paddings is what makes the "morphing". 16px 64px for a stronger stretch; keep the same height (14 → 14 px) for a button that only grows in width. This padding change moves neighboring elements: center the button or give it a fixed width if it sits in a row. |
Corner shape (CSS — border-radius: 14px → 20px) |
14 px then 20 px | 999px on hover gives a pill; the reverse (20 → 8 px) gives a button that hardens under the hand. The transition interpolates both values over 0.4 s. |
Hover color (CSS — border-color, box-shadow in rgba(139,92,246,…)) |
violet at 40% (border), 30% (shadow), 10% (inner glow) | Replace the three occurrences of 139, 92, 246 with your brand color. The inner glow (inset 0 0 20px) is what makes the glass look lit; remove it for a quieter hover. |
Zoom (CSS — transform: scale(1.05) on hover, scale(.98) on click) |
+5% then −2% | The zoom adds to the padding stretch: at 1.05, the 193 px button looks 203 px wide. Use scale(1) so only the stretch plays, with no text blur during the growth. |
Glass (CSS — background: rgba(255,255,255,.08), backdrop-filter: blur(20px)) |
8% white, 20 px blur | Over a light photo or video, switch to a dark tint (rgba(30,25,20,.22)); over a very busy video, raise the blur to 28 px so the label doesn't shimmer over the details. |
Glows behind the button (CSS — .glass-btn-bg-1, .glass-btn-bg-2, glassBtnOrb 5s) |
100 px violet and 80 px cyan, 40 px blur, 5 s | Over an already colorful background, keep them to tint the glass in brand colors or remove them. The dark background comes from .demo-preview: if you remove it, give .glass-btn-scene a background or place it over your image. |
FAQ
transform: scaleX() on a background pseudo-element.<button> — but the stretch is tied to hover. With a finger, on most mobile browsers, the first tap triggers both the click and :hover, which stays applied after release: the button stays stretched. With the keyboard nothing stretches, only the focus ring appears. Add :focus-visible to the hover rule and wrap that rule in @media (hover: hover) for clean behavior on all three.#0a0a0f background on which the glows and the glass read; the .glass-btn-scene itself is transparent. If you place the button over your own image or video, you can remove that frame: the glass will blur your image. If you place it on a white page with nothing behind, keep the frame or give the scene a background, otherwise the white label disappears.