---
title: "A liquid-glass button, and the @property trick behind it"
description: "Petr Knoll's frosted glass button, ported to a component. backdrop-filter for the frost, layered inset rims, and an animated conic-gradient border + sheen powered by @property angle interpolation."
date: "2026-05-11"
tags: ["css", "glassmorphism", "@property", "buttons"]
---

[Petr Knoll's](https://codepen.io/Petr-Knoll/pen/QwWLZdx) glass button has been
making the rounds, and it's worth pulling apart, because almost every effect in
it is a trick worth keeping. Hover and press it (it sits on a light, dotted
panel so the frosting has something to blur):

## The frost

The pill is translucent with a `backdrop-filter: blur`, so it genuinely frosts
whatever sits behind it. That's the part that makes it read as *glass* and not
just a light gradient, and it's why it needs a busy background to show off. Two
inset shadows finish the edge: a soft dark one up top, a brighter one along the
bottom, like light catching the lower rim.

```css
.glass-btn {
  backdrop-filter: blur(clamp(1px, .125em, 4px));
  box-shadow:
    inset 0 .125em .125em rgba(0,0,0,.05),
    inset 0 -.125em .125em rgba(255,255,255,.5),
    0 .25em .125em -.125em rgba(0,0,0,.2);
}
```

## The @property trick

Here's the clever bit. CSS won't animate the angle *inside* a gradient. The
angle is part of an unparsed value, so the browser has nothing to interpolate.
Registering the angle as a real typed custom property fixes that:

```css
@property --glass-1 {
  syntax: "<angle>";
  inherits: false;
  initial-value: -75deg;
}
```

Now `--glass-1` is an actual angle the browser can tween. The button's border is
a `conic-gradient(from var(--glass-1) …)` masked down to a hairline with
`mask-composite: exclude`, and a second registered angle drives a diagonal sheen
that slides across the face. On hover and press, both angles transition, so the
light appears to sweep around the glass instead of snapping.

## The little touches

A separate blurred element under the button is the shadow, and it shifts as you
interact, so the button feels like it lifts on hover. On `:active` the whole
wrap tilts back with `rotate3d(1, 0, 0, 25deg)`, a tiny bit of physicality that
makes the press land. Everything is em-based, so it scales with `font-size`, and
the angle animations quietly freeze on touch devices rather than jumping.

Full credit to [Petr Knoll](https://codepen.io/Petr-Knoll/pen/QwWLZdx). Grab the
component on the [Liquid Glass Button](/components/glass-button) page.
