website-glass
Components

Glass

A liquid-glass lens engine — an SVG displacement filter run as a backdrop-filter physically bends the page through the lens. Exports Glass, GlassLens, and GlassSurface.

drag the lens — it refracts the live page

Playground

Edit the props live and copy the config.

Liquid Glass
The rim bends the content behind it; text stays sharp.
Radius
24
Strength
0.5
Blur
4
Tint
0.25
Dome
0.1
<Glass
  radius={24}
  strength={0.5}
  blur={4}
  tint={0.25}
  dome={0.1}
>
  {children}
</Glass>

Install

npx shadcn@latest add https://websiteglass.com/r/glass.json

This installs two files: components/ui/glass.tsx (the lens engine, GlassLens, and GlassSurface) and components/ui/glass-motion.ts (the motion primitives used by the other components).

The three pieces

  • Glass — a rounded box that refracts whatever sits inside it. Good for cards over busy content.
  • GlassLens — a free-floating lens of a fixed size; refracts the page behind it. Used for draggable lenses and the "lift into glass" thumbs on the switch and slider.
  • GlassSurface — a plain frosted, tinted panel (no displacement) for floating UI like popovers and dialogs, where there's nothing behind it to bend. Renders identically in every browser.

Real refraction (the bending) needs backdrop-filter: url(), which today only Chromium runs. On Safari and Firefox the components fall back to a frosted blur automatically.

Usage

import { Glass } from '@/components/ui/glass';

<div className="relative h-64 overflow-hidden rounded-2xl bg-[url(/photo.jpg)] bg-cover">
  <Glass className="absolute inset-6" radius={20} strength={0.6} contentClassName="p-6">
    <p className="text-white">Content sits above the lens.</p>
  </Glass>
</div>
import { GlassSurface } from '@/components/ui/glass';

<GlassSurface tint={0.5} radius={20} className="p-4">
  <p className="text-white">Frosted panel over any content.</p>
</GlassSurface>

Glass Props

PropTypeDefaultDescription
strengthnumber0.5Refraction strength 0–1. Controls displacement px and rim band.
blurnumber4Backdrop blur in px.
tintnumberderivedTint opacity 0–1. Defaults from strength.
tintColorstringthemeTint colour as an r,g,b triple.
domenumber0Lens dome bulge — a spherical magnification toward the centre.
radiusnumber24Corner radius in px.
lensReactNodeOptional explicit lens layer (e.g. nested GlassLens elements).
className / contentClassNamestringRoot / content wrapper classes.
askeyof JSX.IntrinsicElements"div"Root element tag.

GlassLens Props

PropTypeDefaultDescription
width / heightnumberrequiredLens size in px.
radiusnumber9999Corner radius (default = pill / circle).
strengthnumber0.55Refraction strength 0–1.
blurnumber2Backdrop blur in px.
domenumber0.15Dome bulge.

GlassSurface Props

PropTypeDefaultDescription
tintnumber0.5Panel tint 0–1. Higher = more frosted.
tintColorstringthemeTint color as r,g,b triple. Defaults to white / dark-gray based on color scheme.
blurnumber12Backdrop blur in px.
saturationnumber1.5Backdrop saturation boost.
radiusnumber16Corner radius in px.
specularbooleantrueRender specular rim highlights and corner sheen.
handleRefRefObject<GlassSurfaceHandle>Imperative handle — call setTintLift(delta) to animate tint without re-render.

How it works

A <canvas> generates a displacement map: a PNG whose red and green channels encode the per-pixel X/Y bend. The map is flat (no shift) through the middle and ramps up hard at the rim, following the lens's rounded-rectangle profile, with an optional dome term for centre magnification.

That map feeds an SVG feDisplacementMap filter, which is applied as the element's backdrop-filter. In a backdrop-filter the filter's input is the content behind the element, so the displacement physically bends the page seen through the lens — text underneath stays live and selectable. A frosted blur, tint, sheen gradient, and rim shadow are layered on for the glass material.

backdrop-filter: url() is currently Chromium-only. On Safari and Firefox the components detect this and fall back to a plain frosted blur — still glass, just without the bending.