Skip to content

ihasq/liquidglass.css

Repository files navigation

liquidglass.css

liquidglass.css

Liquid glass effect, as CSS properties. As it should be.

npm version bundle size license


.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;
}

Demo


Features

  • Typed CSS Custom Properties — Registered via @property so 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 Trackingborder-radius is mirrored to --glass-radius via 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

Quick Start

npm install liquidglass.css

One 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>

Tailwind CSS v4

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].

StyleX

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 styles
  • glass.refraction(), glass.thickness(), etc. — individual properties
  • glass.presets.subtle, glass.presets.frosted, etc. — preset styles

Emotion

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>

styled-components

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;
`;

Vanilla Extract

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',
});

Panda CSS

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>

UnoCSS

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>

Parameters

All parameters are registered as typed CSS Custom Properties. Each prefix is --glass-.

Surface

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

Chromatic Aberration

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 aberration
  • crown — Crown glass, low dispersion
  • standard — Standard optical glass
  • flint — Flint glass, moderate dispersion
  • dense-flint — Dense flint, strongest aberration

Specular (CSS Paint Worklet)

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

Renderer (advanced)

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

Dynamic Updates

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');

How It Works

┌──────────────────────────────────────────────────────────────────┐
│                                                                  │
│   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.

Framework Examples

React

import "liquidglass.css";

function GlassCard({ children }) {
  return (
    <div className="glass-card">
      {children}
    </div>
  );
}
.glass-card {
  --glass-refraction: 70%;
  --glass-gloss: 60%;
  border-radius: 20px;
}

Vue

<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>

Vanilla

<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 Support

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.

Performance

  • 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 @property value 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

Roadmap

  • CSS Houdini paint worklet (specular)
  • WebGPU / WebGL2 / WASM-SIMD displacement renderers
  • Typed @property registration 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

Contributing

git clone https://github.com/ihasq/liquidglass.css.git
cd liquidglass.css
npm install
npm run dev

Visit 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 analysis

Acknowledgments

This 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.

License

MIT License.

About

import "liquidglass.css"

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors