iOS-style Live Refraction for HTML Elements – css-liquid-glass

Category: Animation , Javascript | November 9, 2026
Authornordify
Last UpdateNovember 9, 2026
LicenseMIT
Views0 views
iOS-style Live Refraction for HTML Elements – css-liquid-glass

css-liquid-glass is a TypeScript and CSS utility that adds iOS-style live refraction to buttons, pills, and other rounded HTML elements.

It bends the page near its edges, reflects nearby content into the rim, and keeps the original HTML content intact.

Full refraction works in Chromium browsers. Safari and Firefox on display frosted glass.

Features

  • Adjustable corner radius, refracting rim, blur, and saturation.
  • Optional mirrored reflections with adjustable strength.
  • Edge glints, caustic light, tint, and shadow.
  • Nine-slice displacement maps that adapt to width changes.
  • Automatic updates when the glass element is resized.
  • Light and dark styling with opacity-crossfaded cover layers.

How To Use It

Installation

Download and copy src/glass/ into your TypeScript project with a bundler that handles CSS imports.

Basic Usage

Place a transparent button over scrolling page content. With the copied files in src/glass/, put the initialization in src/main.ts. Keep the button outside <main> because the default promote: true adds will-change: transform to main.

<main>
  <h1>Explore the collection</h1>
  <p>Scroll the page to see the background move behind the glass.</p>
</main>
<button class="glass-action" type="button">
  <span>Explore</span>
</button>
main {
  min-height: 180vh;
  padding: 4rem;
  background: linear-gradient(130deg, #f4efff, #9fd7df 55%, #fcba8e);
}
.glass-action {
  position: fixed;
  top: 2rem;
  right: 2rem;
  z-index: 10;
  padding: 14px 28px;
  border: 0;
  background: transparent;
  color: #1b1b1b;
  cursor: pointer;
}
import { createLiquidGlass } from "./glass/liquid-glass";
const button = document.querySelector<HTMLButtonElement>(".glass-action")!;
const glass = createLiquidGlass(button, {
  radius: 30,
  rim: 1.1,
  blur: 2,
  reflect: true,
});

Run the Demo Locally

To run the official demo, clone it and install development dependencies as follows:

git clone https://github.com/nordify/css-liquid-glass.git
cd css-liquid-glass
npm install
npm run dev

Configuration Options

Initialize the effect with createLiquidGlass(host: HTMLElement, options?: LiquidGlassOptions).

OptionDescription
radiusCorner radius in CSS pixels. Default: 30.
rimRelative width of the refracting rim. Default: 1.3.
blurBackdrop blur in CSS pixels. Default: 2.5.
saturateBackdrop saturation multiplier. Default: 1.1.
reflectEnables the mirrored reflection around the rim. Default: true.
reflectStrengthReflection intensity multiplier. 1 uses the physical Fresnel weighting; larger values strengthen it. Default: 2.5.
promotetrue adds will-change: transform to main for scrolling compositing. Pass a CSS selector to promote another element, or false to disable it. Default: true.

Instance Methods and Refraction Detection

MethodDescription
glass.refresh()Requests a reflection redraw for nearby content that changes independently, such as a playing video. Call it on each frame of that content; scrolling is already handled.
glass.update()Repositions the backing fill and covers after the host moves without changing size, such as a fixed-to-absolute position change.
glass.addCover(parent, dark)Creates a cover with light (0) or dark (1) tuning, hides the built-in cover, and returns an HTMLElement. For a nonfixed host, pass its parent element as parent.
glass.destroy()Disconnects resize observation, removes instance event handlers and generated layers, and removes its covers.

CSS Custom Properties

VariableDescription
--lg-sScaling factor for shadow and edge widths. The fallback value is 1, corresponding to a 60px-high pill.
--lg-shadowShadow color. Default: oklch(0.24 0.05 236.8).
--lg-shadow-oShadow opacity. Default: 0.35 in light mode; 0 in dark mode.
--lg-causticColor of the soft caustic light. Default: #fff.
--lg-caustic-oCaustic opacity. Default: 0.7 in light mode; 0 in dark mode.
--lg-underlayWhite veil opacity over the glass. Default: 0.14 in light mode; 0.03 in dark mode.
--lg-tintBackground tint of the refracting pane. Default: transparent.
--lg-glint-expExposure of the lit edge. Default: 1.35 in light mode; 1.45 in dark mode.
--lg-glint-satSaturation of the lit edge. Default: 3.1 in light mode; 2 in dark mode.
--lg-rim-expExposure of the unlit rim. Default: 0.84 in light mode; 1.25 in dark mode.
--lg-rim-satSaturation of the unlit rim. Default: 2.2 in light mode; 1.3 in dark mode.
--lg-specOpacity of the additive rim highlight. Default: 0.13 in light mode; 0.38 in dark mode.
--lg-darkDark-background styling value. Use 0 for light and 1 for dark. Default: 0.

Glass Elements And Dark Styling

Class or attributeDescription
.lg-hostClass added to the original HTML element by initialization.
.lg-coverGenerated sibling containing the veil and additive edge light. Use this class to target cover styling.
data-lg-darkAttribute on the host that activates dark styling for the host and its adjacent built-in cover.

Set veil and additive highlight properties on the .lg-cover sibling:

.glass-action {
  --lg-tint: rgb(240 248 255 / 0.05);
  --lg-shadow-o: 0.22;
  --lg-glint-exp: 1.4;
}
.glass-action + .lg-cover {
  --lg-underlay: 0.2;
  --lg-spec: 0.18;
}

Dark Backgrounds And Theme Transitions

Set data-lg-dark on the host when the glass sits over dark content. You can also set --lg-dark on both the host and its cover.

button.setAttribute("data-lg-dark", "");

For an opacity transition, create two covers and append them to document.body for the fixed button above.

const lightCover = glass.addCover(document.body, 0);
const darkCover = glass.addCover(document.body, 1);
for (const cover of [lightCover, darkCover]) {
  cover.style.willChange = "opacity";
  cover.style.transition = "opacity 250ms ease";
}
lightCover.style.opacity = "1";
darkCover.style.opacity = "0.001";
function setDarkGlass(dark: boolean) {
  button.toggleAttribute("data-lg-dark", dark);
  lightCover.style.opacity = dark ? "0.001" : "1";
  darkCover.style.opacity = dark ? "1" : "0.001";
}

Alternatives And Related Resources

You Might Be Interested In:


Leave a Reply