A TypeScript UI library that delivers highly transparent Liquid Glass through WebGL optics, physical springs, and accessible React primitives.

This is an independent open-source project and is not affiliated with Apple Inc. It references public design principles while providing an original implementation for the web.
Nothing above is a video effect: that is a WebGL displacement map applied to the
live backdrop through backdrop-filter, and the way the shape stretches and
overshoots is a spring reading pointer velocity. Both are on by default. Drag it
yourself at , which also hosts the
component reference and the shadcn registry.
Packages
Quick start
Next.js and React Server Components
Every component needs state, refs or the WebGL lens, so the whole package sits on
the client side of an RSC boundary. The published bundles carry a 'use client'
directive, so importing them straight into a server component works — no wrapper
file needed. Only event handlers have to move: a function cannot cross from a
server component into a client one, so anything with onClick or local state
belongs in its own 'use client' component.
apps/next-example is a working Next.js 16 App Router app whose page is a server
component, and its build runs in CI. If the client boundary ever regresses, that
build fails rather than yours.
Tailwind CSS v4
Import tailwind.css instead of styles.css, before Tailwind itself:
That declares the cascade layer order — so className="rounded-full" on a
LiquidButton actually wins — and bridges the --lq-* tokens into Tailwind's
theme as bg-liquid-accent, text-liquid-muted, rounded-liquid,
shadow-liquid, ease-liquid and friends. The bridge uses @theme inline, which
is what keeps those utilities resolving per-theme at use time.
For coding agents
The MCP tools are get_conventions, list_components, get_component,
search_components, get_component_source, get_tokens, list_icons and
get_core_api. The catalog behind them is generated from source at build time, and
lists only names the package entry point re-exports — so an agent is never told to
import something that does not resolve.
The registry copies real component source into your project rather than a
re-export, with imports rewritten to @/components/ui, @/lib and @/hooks. The
copied tree keeps @liquefy-ui/core and @base-ui/react from npm but not
@liquefy-ui/react, so a copied LiquefyProvider never ends up competing with the
packaged one. Every copied file is written with its own 'use client' directive, so an
RSC app needs no follow-up edit.
Documentation
Everything is on :
Old #/guides/* links redirect to their #/docs/* equivalents.
Style overrides: the styles prop
Every component takes a styles prop for one-off overrides, so reaching for a
stylesheet is optional. It is a superset of style:
Static values ride the style attribute, so the common case adds no stylesheet
and no hydration concerns. As soon as a state or breakpoint appears, the whole
object moves into a generated class instead — otherwise the inline declarations
would outrank the very rules meant to override them. That class is inserted
unlayered, and the component stylesheet lives in @layer liquefy-ui, so
overrides win on cascade order rather than on !important or specificity.
Precedence is styles over the component's own custom properties, and style
over everything — style stays the last-resort escape hatch.
transform and backdrop-filter are written inline by the jelly springs every
frame and cannot be overridden through styles; development builds warn if you
try. Wrap the component and style the wrapper instead.
Building your own component on the same system:
Server rendering: useInsertionEffect does not run on the server, so flush the
collected rules into the document head yourself with getLiquefyStyleSheet().
Static-only styles need nothing — they are already inline.
LiquefyProvider takes className and style but not styles — it owns the
config that styles reads. LiquidToastProvider takes none of the three: it
renders no root element of its own, only the toast viewport. slotStyles is reserved for per-part styling
({ header, body, footer }) and is not implemented yet.
The full version of this — every shorthand, token reference, breakpoint and state
key, plus custom components and server rendering — is at #/docs/styles-prop,
with the token system itself at #/docs/theming.
Components
33 components and 44 icons, each with live demos and a full prop table at .
When WebGL is unavailable, components automatically fall back to the transparent CSS material. Effects default to on everywhere; use the motion and transparency provider props to tone them down.
Use theme="dark", theme="light", or theme="system" on LiquefyProvider to control appearance.
Design notes
- Real edge refraction: a WebGL shader bakes a rounded-rect lens displacement map, applied to the live backdrop through an SVG
feDisplacementMapinsidebackdrop-filter(with per-channel chromatic dispersion). Chromium renders it fully; WebKit and Gecko gracefully fall back to the blurred CSS material. - One shared WebGL context: browsers cap live WebGL contexts (~16 per page), so every component draws through a single hidden GL canvas and blits into its own 2D canvas. Any number of glass components can coexist.
- Jelly physics: scale, skew, and tilt run on deliberately underdamped springs. Pointer velocity is injected into the springs, so fast sweeps make surfaces sway, and press/release produces several visible overshoots. The shader receives the same energy as
u_wobbleand wiggles the rim in sync. - The overlay shader renders SDF-shaped rim light with RGB dispersion, iridescence, pointer glow, press ripples, and a moving sheen — only during interaction and decay, never continuously.
- Glass is intended for interaction and navigation layers rather than primary content.
- Accessibility comes from Base UI: Dialog, Drawer, Menu, Select, Tooltip, Tabs and Accordion are built on
@base-ui/react, which supplies focus trapping and restoration, scroll locking, Escape handling, roving tabindex, typeahead, and collision-aware positioning. liquefy-ui keeps the optics and the springs and stops re-implementing the parts that are easy to get subtly wrong. The keyboard behaviour is asserted inpackages/react/test/keyboard.test.tsxrather than assumed. - React and React DOM are peer dependencies, preventing duplicate React bundles.
- Motion and transparency are always on by default, independent of OS accessibility settings (macOS "Reduce Motion" / "Reduce Transparency" silently flip both media queries in every desktop browser). Toggle them per subtree with
motion={false}/transparency={false}onLiquefyProvider; apps that want to honor the OS can pass e.g.motion={!matchMedia('(prefers-reduced-motion: reduce)').matches}or use the core-levelrespectReducedMotion/respectReducedTransparencyoptions.
Contributing
Issues and pull requests are welcome. rc is the development branch — branch from
it and open the pull request back into it. has
the local setup, the test suites, the CI jobs and the commit convention.
Sponsor
funds the maintenance of this
library — the shader work, the browser matrix, and the release plumbing that keeps
pnpm add @liquefy-ui/react boring. Sponsorship goes to the maintainer rather than
to an organisation; there is one of us.
GitHub takes no cut, so the whole amount arrives. One-off is as welcome as monthly.
License
MIT