Skip to content
← components

Liquid Glass Button

new

A frosted backdrop-blur button with layered inset highlights, an animated conic-gradient border, a moving specular sheen, and a shadow that tilts on press. Ported from Petr Knoll.

hover for the sheen · press to tilt

Usage

example.tsx
import { GlassButton } from "~/components/buttons/glass-button";
<GlassButton onClick={generate}>Generate</GlassButton>

What's going on

This is Petr Knoll's button, and it's a small masterclass in stacking cheap effects. The pill is translucent with a backdrop-filter: blur, so it actually frosts whatever's behind it (that's why it wants a busy, light background to sit on). Two inset shadows, a dark one up top and a light one along the bottom, give it a glassy edge.

The two moving parts both lean on @property. CSS can't normally animate the angle inside a gradient, but registering an angle custom property as <angle> makes it interpolatable. So the conic-gradient border (masked down to a hairline with mask-composite: exclude) and the diagonal sheen across the face both rotate smoothly on hover and press. A separate blurred element underneath is the shadow, and on :active the whole wrap tilts back with rotate3d so it feels like it's being pushed.

It's all em-based, so it scales with font-size, and it degrades on touch (the angle animations freeze rather than jump).

glass-button.css
/* Animatable angles, so the border + sheen can transition smoothly. */
@property --glass-1 { syntax: "<angle>"; inherits: false; initial-value: -75deg; }
@property --glass-2 { syntax: "<angle>"; inherits: false; initial-value: -45deg; }
.glass-btn {
all: unset;
background: linear-gradient(-75deg,
rgba(255,255,255,.05), rgba(255,255,255,.2), rgba(255,255,255,.05));
backdrop-filter: blur(clamp(1px, .125em, 4px)); /* the frost */
box-shadow:
inset 0 .125em .125em rgba(0,0,0,.05),
inset 0 -.125em .125em rgba(255,255,255,.5), /* bottom rim light */
0 .25em .125em -.125em rgba(0,0,0,.2); /* soft lift */
}
/* Conic-gradient border masked to a hairline; the angle animates on hover. */
.glass-btn::after {
background: conic-gradient(from var(--glass-1) at 50% 50%,);
mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
mask-composite: exclude;
}
.glass-btn:hover::after { --glass-1: -125deg; }

Props

proptypedefaultdescription
childrenReactNode"Generate"Button label.
…propsButtonHTMLAttributesAny native button prop (onClick, type, aria-*).

Original by Petr Knoll.