GodUIGodUI
129Follow on X

Anatomy of the Liquid Glass Lens

How a parent-owned pointer, a band=0 displacement map, and a fixed sphere glint turn the same refraction stack into a floating magnifier that never steals clicks.

01/04Disc, fixed glint, full map
frost
fixed glint
band=0

Glint stays at 32% / 28%, a sphere highlight rather than a pointer sheen. The map ramps edge-to-edge (band=0), unlike the card's neutral mid.

Frost
blur + saturate + optional url(#filter)
Glint
fixed radial · not pointer-tracked
Full map
buildDisplacementMap(size, size, 0)
tsx
backgroundImage: `radial-gradient(circle at 32% 28%,rgba(255,255,255,${0.7 * sheen}) 0%,rgba(255,255,255,0) 55%)`boxShadow: `inset 0 2px 2px …, inset 0 -6px 12px …`
01

Disc, fixed glint, full map

LiquidGlassLens shares RefractionFilter and buildDisplacementMap with the card, but its geometry, pointer contract, and reduced-motion policy differ. It is a pointer-events-none circle that follows the cursor over a positioned parent, and it disappears entirely under prefers-reduced-motion.

Think of the card as a frosted pane on your UI; the lens is a loupe you drag across it.

The root is rounded-full with frost via backdrop-filter. Unlike the card's pointer-tracked sheen, the specular is a fixed radial at 32% 28%: a sphere highlight that stays put relative to the disc, plus an inset depth shadow:

The displacement map uses band={0}: a continuous ramp from edge to edge, so the bend feels like a solid converging lens rather than a flat pane with soft rims.

tsx
 
buildDisplacementMap(size, size, 0); // full lens, not rim-only

Defaults push harder than the card (strength={80} vs 60, size={160}) because a smaller circular optic needs more bend to read. Storybook demos often use size={220}.

02/04Parent owns the pointer
relative parent
Parent events
pointermove / enter / leave
Centering
translate(calc(var(--lg-px) - 50%), …)
tsx
node.style.setProperty("--lg-px", `${e.clientX - rect.left}px`);node.style.setProperty("--lg-py", `${e.clientY - rect.top}px`);
02

Parent owns the pointer

Listeners attach to rootRef.current.parentElement, not the lens. The parent must be relative; the lens never blocks clicks (pointer-events-none). On move it writes pixel offsets relative to the parent box, where the card uses percentages:

Centering is a transform that subtracts half the lens size:

tsx
 
transform:
  "translate(calc(var(--lg-px, 0px) - 50%), calc(var(--lg-py, 0px) - 50%))"
className="… [will-change:transform] z-popover …"

Opacity fades with duration-200 while hovering is true (enter / leave on the parent).

tsx
 
parent.addEventListener("pointermove", move);
parent.addEventListener("pointerenter", enter);
parent.addEventListener("pointerleave", leave);
// cleanup removes all three
03/04Rim band vs lens band
card · band 0.3
lens · band 0

Real maps pack X into red and Y into green. Here a single grayscale ramp is enough. The dashed mid is the neutral "no shift" band.

Neutral mid
dashed band = no displacement
Converging lens
continuous ramp edge-to-edge
03

Rim band vs lens band

Same builder, different band. That one number is the entire optical difference. Diagrams use a grayscale ramp (dashed mid = no shift). The SVG map still packs X into red and Y into green, but Learn stays token-neutral:

CardLens
band0.30
Mapneutral mid, bend at rimcontinuous ramp edge-to-edge
Feelflat frosted paneconverging loupe
Specularpointer-tracked sheenfixed sphere glint
Pointer vars--lg-x/y as %--lg-px/py as px
Reduced motionhide sheen, keep frosthide entire lens
04/04Result
04

The result

Hover the stage. The lens tracks without stealing clicks from the content beneath.

The lens runs the card's refraction chain with a different map, a different pointer owner, and a different reduced-motion contract.

Accessibility

The entire root is motion-reduce:hidden. When reduced motion matches, pointer updates are skipped too, since there is nothing to track. Frost / glint / SVG stay aria-hidden. There is no keyboard mode because the lens is a decorative optic. Prefer the card when you need a persistent glass surface for content.

The lens uses the same useRefractionSupport probe as the card: without backdrop-filter: url(), frost is blur + saturate only.

Motion Score

Liquid Glass LensSS: Compositor-only
StransformCompositor transform (translate / scale / rotate)
SopacityFade / cross-fade
Each property is graded by how the browser runs it, from S (composited off the main thread) down to F (layout thrashing); the component takes the worst. MotionScore methodology →