A liquid tab indicator — a spring-driven metaball rendered in raw WebGL2. Press it, drag it between tabs, and watch it squeeze, stretch, split, and settle like a droplet.
Demo & playground: liquid.jerrylinn.com
Ships as a single zero-dependency web component, so it works the same in Vue, React, Astro, and plain HTML.
npm install liquid-dockThe API is HTML: plain anchors as children. Server-renderable, usable before JS loads, and the CSS pill fallback covers browsers without WebGL2.
<liquid-dock current="/works">
<a href="/works">Works</a>
<a href="/resume">Resume</a>
<a href="/about">About</a>
</liquid-dock>Tell the compiler the tag is a custom element
(compilerOptions.isCustomElement: (tag) => tag === 'liquid-dock'), then:
<script setup>
import 'liquid-dock';
</script>
<template>
<liquid-dock :current="route.path">
<a href="/">Home</a>
<a href="/about">About</a>
</liquid-dock>
</template>import 'liquid-dock';
<liquid-dock current={location.pathname}>
<a href="/">Home</a>
<a href="/about">About</a>
</liquid-dock>---
import 'liquid-dock';
---
<liquid-dock current={Astro.url.pathname}>
<a href="/">Home</a>
<a href="/about">About</a>
</liquid-dock>With a router, intercept the cancelable dockchange event and navigate
yourself:
dock.addEventListener('dockchange', (e) => {
e.preventDefault();
navigate(e.detail.href);
});| Attribute | Description |
|---|---|
current |
href of the tab that owns the base |
variant |
dew (default — lit 3D water bead), frost (flat pill) or swift (Apple's Liquid Glass: a lensing rim, hairline highlight and a light/dark film that follows the scene — fixed to the native material, so fill, rim, refract, liquid and the spring knobs are ignored; the tab labels are drawn by the shader and bend at the rim like the scene) |
scheme |
Swift's film: auto (default — flips from the scene behind), light or dark |
tuning |
JSON overrides for the motion table, e.g. '{"liquid": 2}' |
refract |
URL of an image of whatever the dock floats over — Dew and Swift bend it through the base like a lens |
refract-fit |
parent (default — the host paints the same image behind the dock, cover-fitted to the parent) or self (the dock carries its own scene: the plate paints it blurred, the base samples it at natural size) |
Plain CSS custom properties on the element:
liquid-dock {
--ld-ink: #fff; /* tab text color */
--ld-font: Graphik, sans-serif;
}MIT © Jerry Lin