English | 简体中文
A lively, low‑fatigue Liquid Glass theme for the web. Real edge refraction that bends the backdrop (not just a blur), a calm palette that’s easy on the eyes over long sessions, and a specular light that follows the cursor so the glass feels alive — but only while you interact.
Aurora is built on a small, dependency‑free refraction engine that’s also exposed on its own.
- ~4 KB core, no runtime dependencies, framework‑agnostic
- Real refraction via an SVG displacement filter driven into
backdrop-filter - Cursor‑tracking specular highlight — motion only on interaction, honours
prefers-reduced-motion - Adjustable frost (Clear ↔ Frosted), à la iOS 26 / the OS 28 beta
- Calm, low‑saturation tint + soft cool rim → less eye strain than “loud” glass
- First‑class React binding; also a
<script>global build - Degrades gracefully to plain blurred glass where the effect isn’t supported
Runs unchanged anywhere a Chromium engine renders it — Chrome, Edge, and Chromium shells: Electron, Tauri, WebView2 (Windows desktop), and Android WebView (Capacitor / Cordova / React‑Native‑WebView). See Platform support.
npm i aurora-glassimport { Aurora } from 'aurora-glass'
import 'aurora-glass/aurora.css'
const aurora = new Aurora({ targets: '.card' }) // refraction + cursor light + calm palette
aurora.start()
aurora.setFrost(0.4) // Clear ↔ Frosted, any time<div class="card aurora-glass aurora-glass--interactive">…</div>That’s the whole theme: real backdrop refraction, a cursor‑tracking highlight, and a frost you can dial.
import { useAurora } from 'aurora-glass/react'
import 'aurora-glass/aurora.css'
function Card({ frost }: { frost: number }) {
const ref = useAurora<HTMLDivElement>({ frost })
return <div ref={ref} className="aurora-glass aurora-glass--interactive">…</div>
}Option changes (like a frost slider) apply live.
<link rel="stylesheet" href="aurora-glass/aurora.css" />
<script src="aurora-glass/dist/aurora-glass.global.js"></script>
<script>
new AuroraGlass.Aurora({ targets: '.card' }).start()
</script>See demo/index.html for a complete standalone page.
Three layers make the material read as thick, light‑bending glass rather than frosted plastic.
CSS backdrop-filter can take an SVG filter reference. Aurora feeds it an <feDisplacementMap> whose map is generated per surface on a <canvas>:
- Compute the signed distance field (SDF) of the surface’s rounded rectangle.
- Encode a per‑pixel shift in the map’s R (x) / G (y) channels:
128= no shift. - The shift is zero across the flat centre, and near the border it points inward along the SDF gradient — so the backdrop appears to bend at the rim like the rounded edge of real glass. Because the shift is always inward, the filter never samples outside the element, which avoids the streaky edge artifacts a naïve turbulence displacement produces.
The strength across the edge follows a “bullnose” profile — sin(π·xᵇⁱᵃˢ) — neutral at both the centre and the very rim, peaking just inside the edge. That rise‑and‑fall is what makes the edge read as a rounded, volumetric lip instead of a broad smear.
strength
1 | .-''-.
| / \ ← peak just inside the rim
| / \
0 |__/________________\__ → distance from border
centre band rim
A subtle chromatic aberration runs the R/G/B channels at slightly different displacement scales and recombines them, for a faint colour fringe at the edge like a real lens. Aurora keeps saturation lower (152% vs a “loud” 180%) so the whole thing stays calm over long viewing.
PointerGlow writes the pointer’s surface‑local position into CSS variables (--lg-glow-x/y, --lg-glow) so a highlight follows the cursor across the glass. Motion is driven purely by the pointer — no idle animation, which is the usual source of glass fatigue — and it disables itself under prefers-reduced-motion (falling back to a gentle static highlight).
A single frost value 0..1 maps to extra backdrop blur and (with aurora.css) a dark dimming veil capped ~35% — the value Apple uses behind clear glass over bright media. 0 is the transparent, refraction‑only look; higher values trade transparency for legibility.
The engine keeps every surface’s filter in sync with its size (ResizeObserver) and with surfaces added/removed from the DOM (MutationObserver).
The theme. Wires the refraction engine + cursor light with Aurora’s calm defaults
(saturate: 152, chromaticAberration: 0.05, pointerGlow: true). Accepts every
LiquidGlass option below, plus pointerGlow?: boolean and respectReducedMotion?: boolean.
Methods: start(), stop(), setFrost(v), update(patch), refresh().
| option | default | description |
|---|---|---|
targets |
— | CSS selector, Element, or list to refract |
blur |
2 |
centre backdrop blur (px) at frost 0 |
saturate |
180 |
saturation boost (%) |
band |
22 |
edge refraction band width (px) |
bias |
1.8 |
bullnose peak bias (>1 = closer to the rim) |
scale |
38 |
displacement strength (px); max shift ≈ scale/2 |
profile |
'bullnose' |
'bullnose' or 'linear' edge falloff |
chromaticAberration |
0 |
per‑channel dispersion, ~0.05 for a subtle fringe |
frost |
0 |
0..1 clear↔frosted |
frostBlur |
20 |
extra blur (px) at frost 1 |
radius |
'auto' |
corner radius px, or read the element’s computed radius |
quality |
2 |
displacement‑map downsample factor |
isSupported() reports whether the current engine renders SVG filters inside backdrop-filter (Chromium).
start() / stop(). Writes --lg-glow-x, --lg-glow-y, --lg-glow on the hovered surface; the bundled aurora.css renders the highlight for .aurora-glass--interactive.
The refraction relies on SVG filters inside backdrop-filter, which — as of 2026 — is a Chromium‑only feature (not yet in the CSS spec; W3C svgwg#1142 tracks standardization). Everywhere else the engine no‑ops and your surfaces keep the plain blur() saturate() glass from aurora.css.
| Target | Refraction | Notes |
|---|---|---|
| Chrome / Edge / Chromium desktop | ✅ | full effect |
| Windows desktop via Tauri / Electron / WebView2 | ✅ | all Chromium — reuses this package unchanged |
| Android via WebView (Capacitor / Cordova / RN‑WebView) | ✅ | Android System WebView is Chromium — reuses this package unchanged |
| Safari / WebKit (incl. iOS WKWebView) | WebKit #245510 — no SVG‑in‑backdrop‑filter | |
| Firefox | no SVG‑in‑backdrop‑filter |
This is a web package. Truly native (non‑web) apps can achieve the same look but need an entirely different implementation — out of scope here, documented for reference:
- Native Android — real refraction is possible with an AGSL
RuntimeShader(API 33+, Android 13) viaRenderEffect. Unlike the web there’s no automatic backdrop input, so you must capture the background yourself (record it into aGraphicsLayer/RenderNode) and feed it to the shader. Existing libraries do this:Kyant0/AndroidLiquidGlass,Abdullajon1881/LiquidGlass,uginy/react-native-liquid-glass. Fallback to blur below API 33. - Native Windows — WinUI 3 / Composition backdrops (Mica, Acrylic) are blur+tint only;
DisplacementMapEffectandPixelShaderEffectare[NoComposition], so you can’t shade a liveCompositionBackdropBrush. Real backdrop refraction needs a custom D3D11/Win2D pipeline (Desktop Duplication capture → HLSL displacement → composite). High effort, Windows‑only. - Flutter —
BackdropFilter+ImageFilter.shaderrefracts on Android and Windows (with Impeller) but throws on web (Impeller‑only), so one Flutter codebase can’t yet cover web + Windows + Android with parity.
Bottom line: to reuse this exact implementation across web, Windows, and Android, host it in a Chromium shell (Tauri/WebView2 on Windows, WebView on Android) — one codebase, real refraction, zero platform code. Native (non‑web) parity means a separate shader per platform.
<feDisplacementMap scale> interacts with devicePixelRatio; on 2×–3× screens dial scale down. Keep filtered regions small and consider contain: strict — SVG‑in‑backdrop‑filter is a heavy GPU path.
npm install
npm run dev # tsup --watch
npm run build # emit dist/
npm run typecheck # tsc --noEmitOpen demo/index.html directly in a Chromium browser (or serve the repo root) to see the effect live while iterating.
Issues and pull requests are welcome — bug reports, platform‑compatibility notes, and small focused PRs are especially useful. For anything larger (new options, API changes), please open an issue first to discuss the approach.
Extracted from the “Aurora” theme of a self‑hosted control panel, where it was tuned live in‑browser against real wallpapers.
MIT © feifei