Micro-interactions

Lens

A glass lens you drag across live content, bending it through an SVG displacement field with a real bevel and three-channel dispersion. It refracts a live duplicate of its own children rather than the page backdrop — which is the only version of this effect that works in Chrome, Safari and Firefox alike.

Install

# Lens needs no npm packages

Then copy 2 files into your project:

  • components/registry/lens/lens.tsx
  • hooks/use-animation-loop.ts

Props

PropTypeDefaultDescription
sizenumber45Diameter of the disc, measured against the shorter side of whatever it is dropped into rather than in pixels — so the glass keeps its proportion in a wide hero and in a narrow column alike, and can never outgrow the box on either axis. The displacement map is rasterised once at a fixed resolution and stretched to fit, so dragging this is cheap; regenerating a bitmap per frame would cost far more than the pixels are worth.
refractionnumber0.55How far the map pushes the content, and the one number this component is about. It runs through the filter property on a live duplicate rather than through backdrop-filter, because backdrop-filter with an SVG reference is Chromium-only — and CSS.supports reports true in WebKit and Gecko too, so the capability cannot even be detected honestly.
edgeThicknessnumber0.3How much of the radius is bevel. Real glass does nearly all of its bending in the last fifth, which is why displacing evenly across the whole disc gives a soap bubble rather than a lens.
blurnumber0Softening behind the glass, and it ships at zero on purpose. Blur is the fastest way to make a lens look like a smudge — real glass is sharpest dead centre, so anything past about three stops being an optic and becomes a frosted coaster you cannot read through. At zero the blur is dropped from the filter list rather than written as blur(0px), which would still buy a render surface for no visible effect.
chromaticnumber0.22Separation between the three channels' displacement — three real passes, isolated by colour matrices and recombined additively. Scaled by the disc rather than fixed in pixels, so the same setting is the same optic at any size. Glass disperses; a single pass with a tinted edge is a smear pretending to be physics.
magnifynumber1.6Scale applied to the content inside the disc. It is a real transform rather than a displacement, because displacement can only resample pixels that are already there — faking magnification with it softens the entire window, which is exactly what makes most browser lenses read as a smear. Below one it is a fisheye pushed the wrong way: a real optic, and one that looks broken to everyone who is not an optician.
shimmernumber0.35Speed of the specular travelling the rim. It is the only thing here that moves on its own, and it is what stops a parked lens looking like a screenshot.
frictionnumber0.9How much of a throw survives, applied per second rather than per frame so the same flick coasts the same distance on a 60Hz and a 120Hz display.
boundedbooleantrueWhich box holds the lens in, and what it is looking at. On, it stays inside its container and magnifies a live duplicate of the children you handed it. Off, it is portalled to the body and bound to the viewport instead — dropping the container's own clip is not enough, because any ancestor that clips wins and on a real page there is always one — and what it magnifies becomes the whole page. Either boundary resists and reverses rather than stopping dead; a thrown object that halts against an invisible wall reads as a bug rather than as a boundary. The page is duplicated for real, because no browser API lets an element read arbitrary rendered content: backdrop-filter cannot magnify and is Chromium-only. That copy is a snapshot rather than a live tree, re-taken on every grab, so it cannot carry a running animation, canvas pixels or component state.
rimColorstring (hex)"#e9e6ff"The colour of the rim highlight, masked to the outermost two pixels so it reads as the lit edge of the glass instead of a ring drawn on top of it.

What It Demonstrates

Drag
Moving an element by grabbing it, often with momentum when released.
Momentum
Motion that carries velocity, especially after a drag or interruption.
Rubber-banding
Resistance and snap-back when you drag past a boundary (the iOS overscroll feel).
Damping
How quickly a spring settles. Lower damping means more bounce and oscillation.
Blur
A blur filter used to soften an element or mask tiny imperfections.
Mask
Hiding or revealing parts of an element using a shape or gradient — like clip-path, but with soft, fadeable edges.
Hardware acceleration
Animating transform and opacity lets the GPU keep motion smooth.

Related Components

Appears In