Glass surface
A surface of glass put on any box by one attribute: a navigation bar, a card, a side panel.
Documentation
-
Copy to Webflow
Add Geist and Geist Mono to your site's fonts (Site settings, Fonts, Google Fonts) before pasting: Webflow removes any font your site has not installed. -
Add custom CSS
Custom CSS in WebflowThis CSS already ships inside the Webflow embed, so a pasted section needs nothing added. Paste it yourself, in the page or the site custom code, only if the effect runs on several pages. The tutorial explains where.
See the tutorialThe pane is a box of your own: its class gives its size, its padding, its position and itsborder-radius, which the bend follows. Do not give it a background colour, an opacity, a filter or a backdrop filter: the Embed writes its ground. Keep its padding at least as wide as the settingbevel, so the words stay clear of the bend. A parent with an opacity under 100%, a filter or a blend mode stops the pane from seeing the page behind it, and a parent set tooverflow: hiddenstops a sticky pane from staying in the window. -
Add custom JS
Custom JavaScript in WebflowThis script already ships inside the Webflow embed, settings block included. You paste it yourself, before the closing body tag of the page or the site custom code, only when the effect has to run on several pages, and the tutorial says how to choose.
See the tutorial -
Use it on your own elements
How to add a custom attribute in WebflowA custom attribute is a pair, a name and a value, typed in the Settings panel of the element you selected, under Custom attributes. The name is what the script looks for, the value is what it reads. The tutorial shows the panel.
See the tutorialCustom attributesSelect the element on the canvas and open the Settings panel. Under Custom attributes, add a pair: the name on the left, the value on the right.Custom attribute What it does data-glass-surface Goes on the box that becomes a pane of glass, a bar, a card, a panel, and takes no value: leave the value field empty. What passes behind that box is blurred and bent at its edge, and its rim catches the light. The settings below go on the same element. data-glass-surface-mode, data-glass-surface-ready Written by the engine on the pane: refract where the browser paints the bend, Chrome, Edge and the other Chromium browsers, blur where it does not, Safari and Firefox, and ready once it is mounted. Never written by hand; the stylesheet reads the first, and so can a rule of yours. [ Optional ] data-glass-surface-defs Written by the engine on the one hidden svg it adds at the end of the page, which holds a filter per pane. Never written by hand, and removed with the last pane. [ Optional ] data-glass-surface-drift The motion of the preview, read by the stylesheet alone and never by the engine: the pane that carries it wanders slowly around its place, so the glass always has something new to bend. Leave it out and the pane stays where its class puts it; it stops under reduced motion. [ Optional ] SettingsEvery setting is a custom attribute too, added the same way and on the same element. An attribute always wins over the defaults written in the code.Attribute Default What it is data-glass-surface-tint #1e1711 Colour laid over what is seen through the glass; written on the pane as --glass-surface-tint. A dark colour makes smoked glass, which keeps light text readable over a bright photograph; a light one makes frosted white glass, for dark text. Options: any opaque CSS colour; an empty string in the settings block writes nothing and leaves the --glass-surface-tint of your stylesheet. data-glass-surface-rim #ffe6bd Colour of the line of light around the pane and of the glow it leaves on the glass; written as --glass-surface-rim. Options: any opaque CSS colour; an empty string leaves the --glass-surface-rim of your stylesheet. data-glass-surface-density 0.42 Share of the tint laid over the glass, from 0 to 1. It is what keeps the words on the pane readable. data-glass-surface-blur 12 Blur of what is behind the face of the pane, in px. The bevel stays clear of it, so the bend is seen sharp. data-glass-surface-refraction 20 How far the edge bends what is behind, in px: at the very edge the backdrop is read that far towards the inside of the pane. Only where the bend is painted, Chromium. data-glass-surface-bevel 16 Width of the edge that bends, in px, from the edge of the pane inwards. It never takes more than half of the smaller side. data-glass-surface-dispersion 0.08 How much the colours split inside the bend, from 0 to 1: red is bent a little more than green and blue a little less, as a thick edge does. data-glass-surface-glint 0.9 Strength of the light on the rim, from 0 to 1, of the faint line all round as of the bright spot. data-glass-surface-reach 260 How far the light spreads along the rim from where it is, in px. From outside the pane it is also the distance at which the pointer starts to light the nearest edge. data-glass-surface-light both What moves the light. Options: both, the pointer where there is one and the scroll as soon as the page scrolls; pointer; scroll; none, the light stays where the stylesheet rests it, on the top edge.
Two files, no build step.
It runs in the browser only. Load it with a script tag in plain HTML, in a
client-side script in Astro, never in the frontmatter. In React, call
GlassSurface.mount(ref.current) in useEffect and
destroy() in its cleanup.
-
Tune it, either way
Per element with an attribute, or once for the whole site by editingDEFAULTSat the top of the file.Attribute Default What it is Lower Higher data-glass-surface-tint #1e1711 Colour laid over what is seen through the glass; written on the pane as --glass-surface-tint. A dark colour makes smoked glass, which keeps light text readable over a bright photograph; a light one makes frosted white glass, for dark text. Options: any opaque CSS colour; an empty string in the settings block writes nothing and leaves the --glass-surface-tint of your stylesheet. data-glass-surface-rim #ffe6bd Colour of the line of light around the pane and of the glow it leaves on the glass; written as --glass-surface-rim. Options: any opaque CSS colour; an empty string leaves the --glass-surface-rim of your stylesheet. data-glass-surface-density 0.42 Share of the tint laid over the glass, from 0 to 1. It is what keeps the words on the pane readable. At 0 the glass has no colour of its own, and words on it depend on what passes behind Past 0.7 the pane reads as a filled box and little is seen through it data-glass-surface-blur 12 Blur of what is behind the face of the pane, in px. The bevel stays clear of it, so the bend is seen sharp. At 0 the face is clear glass, and words that pass behind stay readable under the words of the pane Past 24 what is behind is a wash of colour, and a thin bar has no face left: the blur fades towards the edge of the pane data-glass-surface-refraction 20 How far the edge bends what is behind, in px: at the very edge the backdrop is read that far towards the inside of the pane. Only where the bend is painted, Chromium. At 0 nothing is bent and the pane is the blur and the rim, in every browser Past the width of bevel the edge starts to mirror what it bends, and words that cross it read backwards; past 60 a thin bar shows the page twice data-glass-surface-bevel 16 Width of the edge that bends, in px, from the edge of the pane inwards. It never takes more than half of the smaller side. Under 8 the bend is a hairline that reads as a border Past 40 the bend reaches the words written on the pane, and a bar is all bevel and no face data-glass-surface-dispersion 0.08 How much the colours split inside the bend, from 0 to 1: red is bent a little more than green and blue a little less, as a thick edge does. At 0 the bend has no fringe, and the filter is three times lighter Past 0.3 every edge that crosses the bevel turns into a rainbow data-glass-surface-glint 0.9 Strength of the light on the rim, from 0 to 1, of the faint line all round as of the bright spot. At 0 the pane has no rim at all At 1 the spot is the full colour of rim data-glass-surface-reach 260 How far the light spreads along the rim from where it is, in px. From outside the pane it is also the distance at which the pointer starts to light the nearest edge. Under 120 the light is a dot Past 600 the whole rim is lit at once and nothing seems to move data-glass-surface-light both What moves the light. Options: both, the pointer where there is one and the scroll as soon as the page scrolls; pointer; scroll; none, the light stays where the stylesheet rests it, on the top edge. -
Drive it from JavaScript
When the page wants to place the light itself, on a pane that opens or to follow something other than the pointer, or to know whether the bend is painted in this browser.const element = document.querySelector('[data-glass-surface]'); element.addEventListener('glasssurface:ready', (e) => { console.log(e.detail); }); const pane = GlassSurface.mount(element); // the pane auto() mounted, or a new one pane.mode(); // 'refract' or 'blur' pane.light(40, 0); // holds the light 40px along the top edge pane.light(); // gives it back to the pointer and the scroll element.addEventListener('glasssurface:ready', (e) => { console.log(e.detail.mode); }); // When the pane leaves the page: pane.destroy(); // removes the filter and what the engine wrote on the pane
-
Fit it to your page
- Structure:
data-glass-surfaceon the box. That is all the engine needs. What the box holds is yours, in any markup and with your own classes: the words and links of a bar, the content of a card. - Make it yours: give the box its shape. Its size, its padding and its
border-radiuscome from your class, and the bend follows the radius, a sharp corner included. The glass is seen when something passes behind it: put the pane over the page,position: stickyorfixed, or over a picture. - The colour of the glass is
tintwithdensity. The preview is smoked glass, a dark tint under cream words. For white frosted glass set a light tint,data-glass-surface-tint="#f4ece0", a density around0.3, and write the words dark. - The light follows a mouse wherever it is on the page, and lights the rim from outside the pane too. When the page scrolls, and on a touch screen, it runs along the top edge, there and back, once for a little more than a screen of scroll.
data-glass-surface-lightchooses:both,pointer,scroll,none. - Where the bend is painted. A filter drawn in SVG inside
backdrop-filteris painted by Chromium: Chrome, Edge, Opera, Brave, Android. Safari, on the Mac and on every iPhone browser, and Firefox are given the pane without the bend: the blur behind the whole box, the tint, the rim and its light. The engine writes which one it is on the pane,data-glass-surface-mode,refractorblur, and a rule of yours can read it. - Words on the pane sit over everything the glass draws. Keep them inside the bevel: the padding of the box is at least the
bevel. - Several panes on a page each get their own filter. A pane is measured again when it changes size, never while the page scrolls.
- One setting for every pane of the site, without opening the file: declare
window.GlassSurfaceSettings = { blur: 16 }in a script placed beforeglass-surface.js. The three levels, least specific first, areDEFAULTS, then that object, then adata-glass-surface-*attribute on the pane itself (data-glass-surface-blur="16",data-glass-surface-light="none"). - What the script writes: on the pane,
data-glass-surface-ready,data-glass-surface-mode, and custom properties: the settings, the filter,--glass-surface-map, and the place of the light,--glass-surface-xand--glass-surface-y. At the end of the body, one hiddensvgmarkeddata-glass-surface-defsthat holds one filter per pane. Nothing is added inside the pane. - From JavaScript:
GlassSurface.mount(element)returns the instance, withmode(),light(x, y)to hold the light at a point of the pane, in px from its top left corner, andlight()to give it back to the pointer and the scroll,refresh()after the radius changed without the size changing, anddestroy(). The pane emitsglasssurface:readyonce mounted. - **
prefers-reduced-motion**: the light does not move, it stays where the stylesheet rests it, on the top edge, and the motion of the preview stops. The blur and the bend stay: they move no faster than the page. - Without the script the pane is the blur, the tint, the rim and the light at rest: the stylesheet alone draws them. A browser with no
backdrop-filterat all gets a denser tint. - A lens is a pane like the others, round and clear. The one of the preview is
border-radius: 50%withdata-glass-surface-blur="0", a density of0.05and a bevel wider than its radius, so the whole disc bends and nothing is frosted. Itsrefractionstays under a third of its radius. Where the bend is not painted, Safari and Firefox, it is a disc of faintly smoked glass with its rim and its light. - The motion of the preview is one attribute the stylesheet reads and the engine does not:
data-glass-surface-drifton a pane makes it wander slowly around its place. The lens carries it, so the glass has something new to bend at every moment on a page that does not scroll; leave it out and the pane stays put. It stops under reduced motion. - The words of the markup are the preview's, a festival that does not exist, and they ship as they are. Put your own in their place.
- On Webflow, add the fonts first. Add Geist and Geist Mono to your site's fonts (Site settings, Fonts, Google Fonts) before pasting: the paste names them, and the Designer drops a font the site does not have.
- On Webflow, the glass shows on the published page. The Designer runs no Embed, so on the canvas the three panes are plain boxes with no ground, where you edit their content like any other, and nothing drifts.
- Structure:
-
Avoid the pitfalls
- **Do not give the pane a background, an
opacity, afilteror abackdrop-filterof its own.** Its ground is the tint the stylesheet writes; a background on top of it hides the glass, and the three others replace or flatten what the pane sees behind itself. - **An ancestor with
opacityunder 1, afilter, amask, aclip-pathor amix-blend-modebecomes all the pane can see.** The glass then blurs and bends only what is inside that ancestor, and nothing of the page behind it. Put the pane outside it. - The pane uses its two pseudo-elements.
::beforeis the rim and::afterthe thickness. A::beforeor an::afterof your own on the same box takes their place: put yours on a child. - The pane needs a position. The stylesheet gives it
position: relativeat zero specificity, so asticky,fixedorabsoluteof your class wins.position: staticwritten on it lets the rim leave the box. - Sticky needs a parent that does not clip. The bar, the lens and the dock of the preview are
position: sticky, for the day the page under them is longer than one screen; a wrapper above them set tooverflow: hiddenorautostops them from staying in the window. - **Keep
bevelunder a third of the smaller side** of a thin bar, or the bar is all edge and what passes behind it is never blurred. - A pane as large as the screen is heavy. The filter is drawn again for every frame of scroll over the whole pane. A bar, a card, a side panel are what it is for; for a full screen veil set
refractionto 0. - A pane that changes size on every frame, an accordion in flight, has its chart drawn again on every frame. Animate a transform on it, or set
refractionto 0 for the time of the move. - In a framework that owns the DOM, mount in an effect and call
GlassSurface.destroy(el)in its cleanup: the engine takes its filter out of the page with it. - Do not judge the fallback in Chrome. To see what Safari and Firefox draw, set
data-glass-surface-refraction="0": it is the same pane.
- **Do not give the pane a background, an
One click on Copy the AI prompt copies everything an assistant needs to build this effect: the complete code, the markup, the dependency, every setting and every pitfall.
What it does
Glass surface is a site utility component for Webflow and vanilla JavaScript, with no library. Also called frosted glass navbar, liquid glass effect or refracted glass backdrop filter.
A surface of glass put on any box by one attribute: a navigation bar, a card, a side panel. What passes behind the box is blurred behind its face and, at its edge, bent, the way the polished bevel of a thick pane bends what is seen through it: a line that crosses the edge is drawn towards the inside, compressed, and its colours split a little. A line of light one pixel wide runs around the pane; it is faint at rest, brighter where a light would catch it, and that light follows the pointer, also from outside the pane, so the edge nearest to the pointer is the one that shines. Where there is no pointer, and whenever the page scrolls, the light runs along the top edge with the scroll. The bend is a chart drawn by the engine for the exact size and corner radius of the box, read by an SVG filter inside backdrop-filter. Chromium paints that; Safari and Firefox do not, and there the pane is the blur, the tint, the rim and its light, with no bend, which is also what the stylesheet gives alone before the script arrives. Nothing is added to the box: the rim and the thickness are its two pseudo-elements, the filter lives in one hidden svg for the page. Under reduced motion the light stays where the stylesheet rests it. Not a gradient blur at the edge of a zone, and not a lens that follows the cursor: a pane, with an edge. Pattern type glassmorphism, frosted glass, liquid glass, refraction, backdrop blur.
Updated
Questions
Put data-glass-surface on your navbar and take its background colour off. The glass surface blurs what passes behind the bar, lays a tint over it and draws a line of light on its rim; place the bar sticky or fixed over the page, since glass over a flat ground shows nothing.
A frosted blur only softens what is behind. The glass surface also bends it at the edge: inside data-glass-surface-bevel, 16 px by default, the page is read up to data-glass-surface-refraction, 20 px, further in, so a line that crosses the edge curves the way it does through a thick pane. data-glass-surface-dispersion splits its colours a little.
The glass surface works there without the bend. An SVG filter inside backdrop-filter is only painted by Chromium browsers, so Safari and Firefox get the blur, the tint, the rim and its light, and the engine writes data-glass-surface-mode="blur" on the pane. Setting data-glass-surface-refraction="0" shows that same fallback in Chrome.
Yes, it is part of the glass surface. The light on the rim follows the pointer from anywhere on the page, so the edge nearest to it shines, and runs along the top edge when the page scrolls or on a touch screen. data-glass-surface-light takes both, pointer, scroll or none, and data-glass-surface-reach sets how far it spreads.
With the tint of the glass surface: data-glass-surface-tint and data-glass-surface-density, 0.42 by default, a smoked glass for light words. For dark words set a light tint and a density near 0.3. data-glass-surface-blur, 12 px by default, blurs what is behind the face of the pane while its edge stays clear.