# liquid-glass Liquid Glass for the web. Rim refraction, lighting and materials after iOS 27, computed from a lens profile instead of drawn by hand. One function for any element, a component for React, and tabs whose thumb lifts into a lens. ```sh npm install @colineapp/liquid-glass ``` | Browser | What you get | | --- | --- | | Chrome, Edge, Arc, Opera and other Chromium browsers | Full glass with rim refraction | | Safari, Firefox | The same material and rim light, without the bend | Refraction needs SVG filters inside `backdrop-filter`, which only Chromium renders. Elsewhere the library leaves the filter out rather than lose the backdrop. ## React ```tsx import { LiquidGlass } from "@colineapp/liquid-glass/react"; import "@colineapp/liquid-glass/styles.css"; export function Toolbar() { return ( ); } ``` ## Any element ```ts import { createLiquidGlass } from "@colineapp/liquid-glass"; import "@colineapp/liquid-glass/styles.css"; const glass = createLiquidGlass(document.querySelector(".toolbar")!, { variant: "clear" }); glass.update({ variant: "regular" }); glass.destroy(); ``` `attachLiquidGlass(element, options)` builds the glass inside an element you render yourself, the way the React component does. ## The surface Put the glass first inside a positioned, rounded element. It takes the element's corner radius, follows its size, and sits behind its content. - Paint nothing over it: no background, border or shadow on the surface. - No `opacity` below 1, `filter`, `mask` or `backdrop-filter` on the surface or its ancestors while the glass shows. Each of those makes a backdrop root, and the glass would only see that element instead of the page. - Animate a surface's position and size, not its scale. A changing scale leaves Chromium's backdrop blank for a few frames. ## Options | Option | Default | | | --- | --- | --- | | `appearance` | `"system"` | `"dark"`, `"light"`, `"system"` (prefers-color-scheme) or `"class"` (dark while `` has the `dark` class) | | `variant` | `"regular"` | `"regular"` diffuses the page so text stays legible; `"clear"` only bends and lights it; `"lens"` is the clear lens a control lifts into (pair with `LENS_GEOMETRY`) | | `floating` | `false` | For menus, popovers and dialogs: diffuses the page more, so text beneath doesn't read through, and the surface casts a shadow (`--liquid-glass-shadow`) | | `interactive` | `false` | For glass that holds controls: the pointer warms the glass beneath it, and a press blooms out from there | | `tone` | | Per-appearance overrides of the variant's filters: `{ dark: { brightness: 0.4 }, light: { tint: 0.5 } }` | | `geometry` | measured from iOS 27 | Rim width, depth and refractive index; the darkened `edge` and how much of the hairline gathers into `glint`s; hairline, side shade and glow widths; `maxFall`, how steeply the rim may bend | | `tint` | near-black / white | Colour washed over the body by the tone's `tint` share. Any CSS colour, including `var()` | | `hue` | none | A colour whose hue the glass takes on. Neutral colours leave it untinted | | `hueStrength` | 1 light, 0.5 dark | How much of the hue it takes | | `fit` | `"box"` | `"box"` fills the surface, `"fixed"` fills a transformed popper wrapper, `"scroll"` spans a surface that scrolls itself | | `refraction` | `true` | Bend the page at the rim where the browser supports it | | `samples` | `1` | Refraction samples per pixel. `4` antialiases the bend, so labels it stretches don't stair-step; it costs four passes, so keep it for small glass like a lens | `glass.magnify(factor)` magnifies what is beneath the glass about its centre, as a raised lens does (1 for none). It is cheap enough to call every frame, and sharpens what it magnifies so it doesn't come out soft. ## Controls on glass Controls on a glass surface shouldn't get glass of their own. Give the selected or hovered one the `liquid-glass-well` class and it brightens the glass beneath it. ```tsx ``` ## Tabs The track is a glass capsule and the selected tab a well in it. The motion is iOS 27's tab bar, measured from UIKit recordings. Choosing a tab, one thumb lifts off the track into a clear lens that magnifies the labels beneath it, stretches along its path with its speed, and squashes as it drops back in. The bar swells while pressed, and the press lights the glass under it. Pressing the selected tab lifts it in place, and dragging it scrubs across the others. A change of tab the viewer didn't make, such as a route change, slides flat. ```tsx import { useLiquidGlassTabs } from "@colineapp/liquid-glass/react"; export function Period({ value, onChange }) { const track = useRef(null); useLiquidGlassTabs(track); return (