Skip to content

About

import "liquidglass.css"

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

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 Tracking — border-radius is mirrored to --glass-radius for tracked glass elements
  • Tailwind CSS v4 — Native plugin via @plugin "liquidglass.css/tailwind"
  • 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/tailwind";
<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>

Compatibility

liquidglass.css is Chromium-first.

  • Full SVG backdrop displacement path: Chromium / Edge
  • Other browsers: graceful fallback without full displacement
  • CSS Paint Worklet specular highlight requires Paint Worklet support
  • WebGPU is optional; WebGL2 and WASM backends are fallback paths
  • SSR / Node import is safe, but visual initialization runs only in browsers

Unsupported browsers gracefully fall back to backdrop-filter: blur(20px). If CSS Paint Worklet is unavailable, the specular highlight is skipped. The base glass blur/refraction fallback remains active.

Runtime state is exposed for custom fallbacks:

:root {
  --glass-force-svg-backdrop-filter: 0; /* 0 disables, 1 forces */
}

:root[data-liquidglass-backdrop-svg="0"] .glass-panel {
  backdrop-filter: blur(20px);
}

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

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages