GlassModal

A sheet of glass over the page, on a native dialog so the browser handles the hard part.

open it, then press escape

Paste into Claude Code, Cursor or any agent. Pinned to a tag, so it will not change under you.

Options

blur 18

0 to 40

Blur of the page showing through. Past about 24 the page behind stops being recognisable, and a modal whose backdrop is unrecognisable may as well be opaque.

tint 0.72

0 to 1

How milky the sheet is. This is the contrast control and a modal needs more of it than a nav does, because a modal carries body copy rather than three words of navigation.

refract true

true · false

Bend the backdrop as well as blurring it. Chromium only. Everywhere else you get the blur and the edge, which is most of the effect, and nothing looks broken.

refraction 22

0 to 60

How far the rim bends what is behind it, in pixels. A modal can take more of this than a nav because its edge is further from the text.

lightDismiss true

true · false

Close when the page behind is clicked. Leave it on unless the dialog is asking something that should not be dismissed by accident, in which case the question is whether it should be a dialog at all.

What it needs

Category
Navigation, a component
Dependencies
react@>=18
Browser
No WebGL
Frame budget
~0.1 ms · 0 context
Pinned to
v0.12.0
  • A native dialog opened with showModal(), so the top layer, the backdrop, the focus trap, the inert background, Escape and returning focus to the opener are all the browser's. There is no focus-trap library here and there should not be one
  • showModal() does not stop the page behind from scrolling, which is the one thing it leaves you, so the component locks the body and puts it back on close
  • The blur is backdrop-filter, which every current browser supports. The refraction is an SVG filter inside backdrop-filter, which only Chromium applies: Safari ignores it and Firefox parses the value, passes an @supports test and renders nothing
  • A dialog is display:none when shut, so an opening transition has nothing to run between. It animates on a class either side of the frame and waits for animationend before calling close(), with a timer behind it because an interrupted animation never fires one
  • Escape fires cancel rather than close, so that is what is intercepted. The default would skip the leaving animation and never tell the parent the dialog had gone
  • prefers-reduced-transparency turns the sheet opaque and takes the specular with it. forced-colors falls back to a border, which is the one thing that mode keeps
  • Nothing runs per frame. The glass is CSS and the lens map is drawn once per resize at quarter scale
  • backdrop-filter is not free: it forces the backdrop into its own layer. A modal is one element and it is only there while it is open