Monorepo for liquid glass / refraction effects across React, Vue, and Svelte.
@lollipopkit/liquid-glassCore algorithms, shared types, surface presets@lollipopkit/liquid-glass-viteVite plugin and resource-orientedvirtual:modules@lollipopkit/liquid-glass-reactReact components@lollipopkit/liquid-glass-vueVue components@lollipopkit/liquid-glass-svelteSvelte components
npm install
npm run typecheck
npm run buildbun install
bun run typecheck
bun run buildnpm run demo:dev
npm run demo:build
npm run demo:previewThe migrated Svelte preview lives in apps/liquid-glass-demo and renders the
workspace sources for @lollipopkit/liquid-glass-svelte and
@lollipopkit/liquid-glass-vite through local Vite aliases.
Separate non-Vite verification fixtures live in:
apps/liquid-glass-nextvalidates the React package in a real Next.js host using the runtime pathapps/liquid-glass-webpackvalidates the React package in a traditional Webpack 5 host using the runtime path
Framework-specific Vite examples also live in:
apps/liquid-glass-react-demodemonstrates React with registered static assets and runtime fallbackapps/liquid-glass-vue-demodemonstrates Vue with registered static assets and runtime fallback
The React, Vue, and Svelte packages now work without Vite by falling back to runtime asset generation.
If you still want build-time static assets, register the plugin from
@lollipopkit/liquid-glass-vite in your app and wire those assets into the
framework package you use.
import { defineConfig } from "vite";
import { liquidGlassPlugin } from "@lollipopkit/liquid-glass-vite";
export default defineConfig({
plugins: [liquidGlassPlugin()],
});import { registerLiquidGlassStaticAssets } from "virtual:liquidGlassStaticAssetRegistry";
import { configureLiquidGlassStaticAssets } from "@lollipopkit/liquid-glass-react";
registerLiquidGlassStaticAssets(configureLiquidGlassStaticAssets);For most apps:
- start with
mode="auto" - do not install
@lollipopkit/liquid-glass-viteunless you want pre-generated static assets - only add the Vite static registry path when startup cost or repeated renders justify it
Current mental model:
- framework packages work on their own
- runtime is the default fallback
- Vite is an optional static optimization layer
- worker runtime no longer depends on
?worker
The Vite package exposes resource modules instead of framework-specific filter components:
virtual:liquidGlassDisplacementMap?...virtual:liquidGlassSpecularMap?...virtual:liquidGlassMagnifyingMap?...virtual:liquidGlassFilterAssets?...virtual:liquidGlassStaticAssetRegistry
virtual:liquidGlassStaticAssetRegistry exports:
import staticAssets, {
registerLiquidGlassStaticAssets,
staticAssets as namedStaticAssets,
} from "virtual:liquidGlassStaticAssetRegistry";virtual:liquidGlassFilterAssets returns:
type LiquidGlassFilterAssets = {
displacementUrl: string;
specularUrl: string;
magnifyingUrl?: string;
width: number;
height: number;
maxDisplacement: number;
magnify: boolean;
params: LiquidGlassFilterParams;
};All framework packages export:
LiquidSearchboxLiquidSliderLiquidSwitchLiquidMagnifyingGlassLiquidParallaxHero
@lollipopkit/liquid-glass also exposes a browser runtime API for generating
filter assets directly at runtime:
import {
configureLiquidGlassWorkerRuntime,
createManagedLiquidGlassRuntimeAssets,
createLiquidGlassRuntimeAssets,
prewarmLiquidGlassManagedRuntimeAssets,
resolveLiquidGlassRuntimeBackend,
} from "@lollipopkit/liquid-glass";
const backend = resolveLiquidGlassRuntimeBackend(
{
width: 360,
height: 120,
radius: 36,
bezelWidth: 24,
glassThickness: 90,
refractiveIndex: 1.5,
magnify: false,
bezelType: "convex_squircle",
},
window.devicePixelRatio,
"auto",
false
);
const assets = await createLiquidGlassRuntimeAssets(
{
width: 360,
height: 120,
radius: 36,
},
{
backend: "auto",
dpr: window.devicePixelRatio,
useCache: true,
}
);
await prewarmLiquidGlassManagedRuntimeAssets(
{
width: 720,
height: 220,
radius: 44,
},
{
backend: "auto",
dpr: window.devicePixelRatio,
useCache: true,
}
);
const managedAssets = await createManagedLiquidGlassRuntimeAssets(
{
width: 360,
height: 120,
radius: 36,
},
{
backend: "auto",
dpr: window.devicePixelRatio,
useCache: true,
}
);If your bundler needs a custom worker loader, configure it before using the managed runtime helpers:
configureLiquidGlassWorkerRuntime({
workerFactory: (options) =>
new Worker(new URL("./liquidGlassRuntime.worker.js", import.meta.url), {
name: options?.name,
type: "module",
}),
});Runtime options:
backend?: "auto" | "ts" | "worker"useCache?: boolean
Notes:
backend="auto"lets applications choose a backend from workload size.backend="ts"forces the main-thread runtime path.backend="worker"prefers the shared worker runtime when the browser supports the required APIs.useCache=falsebypasses runtime cache and forces fresh generation.
Shared runtime helpers:
createLiquidGlassRuntimeAssets(): main-thread runtime helpercreateManagedLiquidGlassRuntimeAssets(): sharedts/workerruntime helperprewarmLiquidGlassManagedRuntimeAssets(): shared worker prewarm helperconfigureLiquidGlassWorkerRuntime(): optional worker factory override for non-default bundler wiring
These runtime helpers are also re-exported from the React, Vue, and Svelte packages so framework consumers can keep using a single package entrypoint.
Run this before publishing packages:
npm run prepublish:checkIt verifies:
- all package builds
- Svelte, React, and Vue Vite demos
- Next.js and Webpack non-Vite fixtures
npm pack --dry-runfor every publishable package
All framework components support:
mode="auto": use registered static assets when available, otherwise fall back to runtime assetsmode="static": prefer registered static assets, but still falls back to runtime when no static assets were configuredmode="runtime": force runtime assets
runtime remains available as a compatibility flag, but mode is now the
preferred API.
The current integration model supports:
- Vite apps using
virtual:liquidGlassStaticAssetRegistry - non-Vite apps using runtime mode only
- custom worker wiring via
configureLiquidGlassWorkerRuntime()when a host bundler needs it