Liquid glass effect, as CSS properties. As it should be.
.card {
--glass-refraction: 80%;
--glass-thickness: 50%;
--glass-softness: 15%;
--glass-gloss: 60%;
--glass-saturation: 45%;
--glass-dispersion: 30%;
--glass-chromatic-aberration: 50%;
--glass-type: dense-flint;
--glass-specular-angle: -60deg;
--glass-specular-width: 2px;
border-radius: 20px;
}- Typed CSS Custom Properties — Registered via
@propertyso values accept their canonical units (<length>,<percentage>,<angle>) - Physics-Based — Snell's law refraction + Cauchy chromatic dispersion
- Chromatic Aberration — Wavelength-dependent RGB displacement with glass material presets
- GPU-First Renderer — WebGPU → WebGL2 → WASM-SIMD auto-fallback
- CSS Paint Worklet Specular — Phong highlight via Canvas2D, no per-pixel loop, repaints on geometry/parameter change automatically
- Auto Radius Tracking —
border-radiusis mirrored to--glass-radiusvia a global MutationObserver + ResizeObserver - Tailwind CSS v4 — Native plugin via
@plugin "liquidglass.css" - CSS-in-JS — First-class support for Emotion, styled-components, StyleX, Vanilla Extract, Panda CSS, UnoCSS
- Framework Agnostic — Works with React, Vue, Svelte, or vanilla
- Adaptive Performance — Smart throttling at scale
npm install liquidglass.cssOne import. That's it.
import "liquidglass.css";.glass-panel {
--glass-refraction: 60%;
--glass-thickness: 50%;
--glass-softness: 15%;
border-radius: 24px;
}border-radius is observed automatically — you don't have to keep
--glass-radius in sync. Bare numbers (e.g. 60 instead of
60%) are also accepted for backward compatibility.
<div class="glass-panel">
Your content here
</div>Native plugin support — same import, just add @plugin in CSS.
import "liquidglass.css";@import "tailwindcss";
@plugin "liquidglass.css";<div class="glass-refraction-[80%] glass-thickness-50 glass-softness-[15%] rounded-2xl">
Your content here
</div>All parameters are available as utilities:
| Utility | CSS Property |
|---|---|
glass-refraction-{value} |
--glass-refraction |
glass-thickness-{value} |
--glass-thickness |
glass-softness-{value} |
--glass-softness |
glass-gloss-{value} |
--glass-gloss |
glass-saturation-{value} |
--glass-saturation |
glass-dispersion-{value} |
--glass-dispersion |
glass-chromatic-aberration-{value} |
--glass-chromatic-aberration |
glass-type-{value} |
--glass-type |
glass-specular-angle-{value} |
--glass-specular-angle |
glass-specular-width-{value} |
--glass-specular-width |
glass-specular-shininess-{value} |
--glass-specular-shininess |
glass-{value} |
--glass-refraction (shorthand) |
Arbitrary values work: glass-refraction-[73%], glass-specular-angle-[-45deg]}, glass-type-[dense-flint].
Type-safe glass styles for Meta's StyleX.
import "liquidglass.css";import * as stylex from '@stylexjs/stylex';
import { glass } from 'liquidglass.css/stylex';
const styles = stylex.create({
// Composite styles
card: {
...glass({
refraction: '80%',
thickness: '50%',
softness: '15%',
}),
borderRadius: '20px',
},
// Presets
frosted: {
...glass.presets.frosted,
borderRadius: '16px',
},
// Individual properties
custom: {
...glass.refraction('60%'),
...glass.thickness('40%'),
}
});
<div {...stylex.props(styles.card)}>Content</div>API:
glass({ ... })— composite stylesglass.refraction(),glass.thickness(), etc. — individual propertiesglass.presets.subtle,glass.presets.frosted, etc. — preset styles
Single import — CSS engine auto-initializes.
import { css } from '@emotion/react';
import { glass } from 'liquidglass.css/emotion';
const cardStyle = css({
...glass({ refraction: '80%', thickness: '50%' }),
borderRadius: '20px',
});
const frostedStyle = css({
...glass.presets.frosted,
borderRadius: '16px',
});
<div css={cardStyle}>Content</div>Single import — CSS engine auto-initializes.
import styled from 'styled-components';
import { glass } from 'liquidglass.css/styled-components';
// Object syntax
const Card = styled.div({
...glass({ refraction: '80%', thickness: '50%' }),
borderRadius: '20px',
});
// Template literal syntax
const FrostedCard = styled.div`
${glass.presets.frosted}
border-radius: 16px;
`;Build-time CSS — import the runtime separately.
import "liquidglass.css";// styles.css.ts
import { style } from '@vanilla-extract/css';
import { glass } from 'liquidglass.css/vanilla-extract';
export const card = style({
...glass({ refraction: '80%', thickness: '50%' }),
borderRadius: '20px',
});
export const frosted = style({
...glass.presets.frosted,
borderRadius: '16px',
});Build-time CSS — import the runtime separately.
import "liquidglass.css";// panda.config.ts
import { defineConfig } from '@pandacss/dev';
import { glassPreset } from 'liquidglass.css/panda';
export default defineConfig({
presets: ['@pandacss/preset-base', glassPreset],
// ...
});// app.tsx
import { css } from '../styled-system/css';
<div className={css({
glassRefraction: '80%',
glassThickness: '50%',
borderRadius: '20px',
})}>
Content
</div>
// Or use preset recipes
<div className={css({ glass: 'frosted', borderRadius: 'xl' })}>
Content
</div>Build-time CSS — import the runtime separately.
import "liquidglass.css";// uno.config.ts
import { defineConfig } from 'unocss';
import { presetGlass } from 'liquidglass.css/unocss';
export default defineConfig({
presets: [presetGlass()],
});<div class="glass-refraction-80 glass-thickness-50 rounded-2xl">
Content
</div>
<!-- Arbitrary values -->
<div class="glass-refraction-[73%] glass-specular-angle-[-45deg]">
Content
</div>
<!-- Presets -->
<div class="glass-preset-frosted rounded-xl">
Content
</div>All parameters are registered as typed CSS Custom Properties. Each
prefix is --glass-.
| Property | Unit | Range | Default | Description |
|---|---|---|---|---|
refraction |
<percentage> |
0–100 | 100 | Lens distortion intensity |
thickness |
<percentage> |
0–100 | 50 | Edge steepness / glass depth |
softness |
<percentage> |
0–100 | 10 | Background blur amount |
gloss |
<percentage> |
0–100 | 100 | Specular highlight intensity |
saturation |
<percentage> |
0–100 | 45 | Color saturation boost |
dispersion |
<percentage> |
0–100 | 30 | Slope-based edge blur |
| Property | Unit | Range | Default | Description |
|---|---|---|---|---|
chromatic-aberration |
<percentage> |
0–100 | 0 | Color fringing intensity (Cauchy dispersion) |
type |
<ident> |
see below | standard |
Glass material preset |
Glass Types (ordered by dispersion strength):
silica— Fused silica, minimal aberrationcrown— Crown glass, low dispersionstandard— Standard optical glassflint— Flint glass, moderate dispersiondense-flint— Dense flint, strongest aberration
| Property | Unit | Range | Default | Description |
|---|---|---|---|---|
specular-angle |
<angle> |
-180–180 | -60deg | Light direction |
specular-width |
<length> |
1–50 | 2px | Bezel highlight width |
specular-shininess |
<number> |
1–128 | 8 | Phong exponent |
| Property | Values | Default | Description |
|---|---|---|---|
displacement-renderer |
gpu | gl2 | wasm |
gpu |
Displacement backend (auto-fallback) |
displacement-resolution |
<percentage> |
40 | Map resolution |
displacement-min-resolution |
<percentage> |
10 | Min resolution during resize |
displacement-smoothing |
<percentage> |
0 | Map smoothing blur |
displacement-refresh-interval |
<integer> |
12 | Frame skip during resize |
enable-optimization |
0 | 1 | 1 | Master optimization toggle |
Parameters respond to any CSS change:
/* Hover state */
.glass-panel:hover {
--glass-refraction: 80%;
}
/* Media queries */
@media (prefers-reduced-motion: reduce) {
.glass-panel {
--glass-refraction: 0%;
}
}
/* Complex selectors */
.container > div:nth-child(2) {
--glass-refraction: 60%;
}Or via JavaScript:
element.style.setProperty('--glass-refraction', '90%');
element.style.setProperty('--glass-specular-angle', '45deg');
element.style.setProperty('--glass-specular-width', '3px');┌──────────────────────────────────────────────────────────────────┐
│ │
│ 1. Renderer (WebGPU / WebGL2 / WASM-SIMD) generates the │
│ displacement map from border-radius │
│ ↓ │
│ 2. SVG filter chain applies displacement: │
│ • Standard: single feDisplacementMap │
│ • Chromatic: RGB separation → 3× displacement → recombine │
│ ↓ │
│ 3. CSS Paint Worklet draws the Phong specular bezel directly │
│ onto the element via Canvas2D — no SVG primitive │
│ ↓ │
│ 4. Adaptive throttling and predictive resize keep it smooth │
│ │
└──────────────────────────────────────────────────────────────────┘
The displacement map encodes refraction vectors:
- Red channel → X displacement
- Green channel → Y displacement
Chromatic Aberration uses Cauchy's dispersion equation n(λ) = A + B/λ²
to calculate wavelength-dependent displacement for R/G/B channels, creating
physically accurate color fringing at glass edges.
Specular runs on its own paint worklet, so changing
--glass-specular-* does not invalidate the displacement
bitmap — and resizing does not rerun the specular path.
import "liquidglass.css";
function GlassCard({ children }) {
return (
<div className="glass-card">
{children}
</div>
);
}.glass-card {
--glass-refraction: 70%;
--glass-gloss: 60%;
border-radius: 20px;
}<script setup>
import "liquidglass.css";
</script>
<template>
<div class="glass-panel">
<slot />
</div>
</template>
<style scoped>
.glass-panel {
--glass-refraction: 70%;
--glass-gloss: 60%;
border-radius: 16px;
}
</style><script type="module">
import "https://unpkg.com/liquidglass.css/dist/liquidglass.js";
</script>
<div style="
--glass-refraction: 80%;
--glass-softness: 20%;
border-radius: 24px;
">
Content
</div>| Browser | Version | Notes |
|---|---|---|
| Chrome | 105+ | Full support (CSS Paint API + backdrop-filter SVG) |
| Edge | 105+ | Full support |
| Safari | — | backdrop-filter SVG not supported |
| Firefox | — | backdrop-filter SVG / CSS Paint API not supported |
Note: Unsupported browsers gracefully fall back to
backdrop-filter: blur(20px). The specular bezel layer simply doesn't render when CSS Paint API is missing.
- GPU-First Renderer — WebGPU when available, WebGL2 next, WASM-SIMD as last resort. Switch explicitly via
--glass-displacement-renderer: gl2;etc. - CSS Paint API Specular — The browser caches and re-invokes the worklet only when a registered
@propertyvalue or geometry changes. No JS render loop for highlights. - Adaptive Throttling — Rendering intervals scale with element count
- Viewport Culling — Off-screen elements pause updates
- Predictive Rendering — Anticipates size changes during resize
- Morph Transitions — Crossfade prevents jarring updates
- CSS Houdini paint worklet (specular)
- WebGPU / WebGL2 / WASM-SIMD displacement renderers
- Typed
@propertyregistration for all parameters - Tailwind CSS v4 native plugin
- CSS-in-JS integrations (Emotion, styled-components, StyleX, Vanilla Extract, Panda CSS, UnoCSS)
- Chromatic aberration with Cauchy dispersion
- Firefox/Safari support via canvas fallback
- Animated displacement maps
- Custom displacement textures
git clone https://github.com/ihasq/liquidglass.css.git
cd liquidglass.css
npm install
npm run devVisit liquidglass-css.pages.dev/labs/
to experiment with parameters live, or run cd site && npm run dev locally.
Quality gates:
npm run build # tsc + vite lib build
npm run test # type / build / SMT / e2e integrity suite
npx knip # static dead-code analysisThis project would not have been possible without the groundbreaking work of @KubeKhrm and kube.io.
His invention goes far beyond simply applying displacement via SVG filters. He identified the precise mathematical formulas required to simulate physically-accurate glass refraction — deriving how border-radius geometry translates into displacement vectors that create the characteristic lens distortion effect. The core technique of encoding these computed refraction vectors into RGB channels and applying them via feDisplacementMap — the very heart of liquidglass.css — originated from his brilliant research.
We stand on the shoulders of giants. Thank you, Kube.
MIT License.
