Frosted Glass Navbar
A frosted navigation bar stuck to the top of a scrolling area: at rest, a 12 px blur and an 8% white veil; from 10 px of scrolling, the glass thickens — 24 px blur, saturation ×2, 15% veil — in 0.4 s, so the text passing underneath no longer gets in the way of the links.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Glassmorphism has 47 effects, including 6 free. Explore the category →
3 usage examples



How it works
A 280 × 180 px .lg-navbar-scene area (12 px corners, indigo gradient at 160°) that the script switches to overflow: auto, a 38 px .lg-frosted-nav bar at the top, and the content below. The bar is a pane of glass: backdrop-filter: blur(12px) saturate(1.6) (with its -webkit- form), 8% white fill, 12% white rule at the bottom. It is position: sticky; top: 0 with margin-bottom: -38px: it stays at the top of the area throughout the scroll, and the negative margin makes it sit over the content instead of pushing it down — so the text slides under the glass. The content keeps a 50 px top padding so it does not start hidden.
The script (7 lines, no dependency) walks every [data-navbar-scene] on the page, listens to its scrolling and adds the .scrolled class to the bar as soon as scrollTop exceeds 10 px, removing it below. .scrolled doubles the blur (24 px), pushes saturation to 2, raises the veil to 15% and the rule to 20%; the 0.4 s transition covers backdrop-filter and background. Measured 150 ms after crossing the threshold: 19.4 px blur and 1.85 saturation in Chromium 151 and Firefox 153, 19.1 px in WebKit 26.5 — all three interpolate the blur; at 600 ms, 24 px everywhere; back at 5 px of scrolling, the class drops. The script also adds 120 px of bottom padding to the content, which gives the demo 90 px of travel.
Why position: sticky with a negative margin rather than position: absolute: inside a scrolling area, an absolutely positioned element is placed relative to the area's content and scrolls with it. Measured in a blank page, an absolute bar would move up 60 px at 60 px of scrolling, out of the area, and 90 px at the end of the travel — the thickened glass would only show between 10 and 38 px, then the bar would vanish with its links. Sticky, it stays at 0 px from the top at 0, 60 and 90 px of scrolling, in Chromium, Firefox and WebKit; the −38 px margin makes it float over the content instead of pushing it down.
Accessibility
- Links: the three “Home / About / Contact” entries are
spans (cursor: default): measured, none receives keyboard focus and a screen reader does not announce them as links. On your site, replace them with<a href>inside a<nav aria-label="Main">, add a:focus-visiblestyle (2 px white outline) and mark the current page witharia-current="page". - Text size and contrast: the links are 8.8 px (
.55rem) and the brand 11.2 px — too small for navigation; raise them to 12-14 px. Links in 80% white: 6.5:1 at rest over the#312e81violet, 5.4:1 once the bar thickens, but 4.1:1 then 3.6:1 over the#4f46e5part of the gradient. The whiter veil slightly lowers contrast over a flat background; what it gains is that the text scrolling underneath becomes a blurred smudge instead of letters overlapping the links. - Motion: the 0.4 s transition only fires on scroll, at the reader's request. The “Scroll me” hint, however, blinks forever (opacity 0.4 ↔ 0.9 every 2 s): add
@media (prefers-reduced-motion: reduce) { .lg-navbar-scroll-hint { animation: none } }, or remove it. Its 8 px text in 40% white is also unreadable: 2.3:1 at best over the bottom of the gradient, 1.6:1 over its lightest part (#818cf8). - Scrolling area: in the demo, the 180 px area scrolls, not the page. Firefox and Safari only give it keyboard focus with
tabindex="0"; on your site, rather apply the effect to the whole page's scrolling (see the FAQ), which the keyboard already knows how to scroll. - Reduced transparency: nothing is provided for
prefers-reduced-transparency; a solid fill there would keep the links readable whatever the content underneath:.lg-frosted-nav, .lg-frosted-nav.scrolled { background: #1e1b4b; backdrop-filter: none }.
Browser compatibility
CSS: backdrop-filter (and -webkit-backdrop-filter), a transition on backdrop-filter, position: sticky, linear gradient, opacity animation. JS: querySelectorAll, addEventListener('scroll'), classList. Zero dependencies. Measured in Chromium 151, Firefox 153 and WebKit 26.5: bar stuck to the top from start to end, .scrolled class set past 10 px and removed below, blur interpolated from 12 to 24 px in all three, no console error.
Without backdrop-filter (Firefox before 103, Chrome before 76), the bar remains an 8% then 15% white veil with no blur: the thickening still shows, but the text passing underneath stays sharp. For those browsers, give .scrolled a more opaque fill inside @supports not (backdrop-filter: blur(1px)). position: sticky has existed since 2017 (Chrome 56, Firefox 59, Safari 13).
The code
HTML structure to paste into your page (the CSS + JS come with Premium):
<div class="demo-preview">
<div class="lg-navbar-scene" data-navbar-scene="">
<div class="lg-frosted-nav">
<span class="lg-nav-brand">…</span>
<div class="lg-nav-items">
<span>…</span>
<span>…</span>
<span>…</span>
</div>
</div>
<div class="lg-navbar-content">…</div>
<div class="lg-navbar-scroll-hint">…</div>
</div>
</div>
The full HTML + CSS + JS of Frosted Glass Navbar, ready to paste — plus all 840 effects. Or $299 once, lifetime access.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Trigger threshold (JS — e.scrollTop > 10) |
10 px | Higher (for instance the height of your header photo, 250 px), the bar stays light while the photo is under it and only thickens when the text arrives. 0 makes it react at the first pixel. |
Glass at rest and thickened (CSS — .lg-frosted-nav and .lg-frosted-nav.scrolled) |
rest: 12 px blur, saturate 1.6, 8% white; thickened: 24 px blur, saturate 2, 15% white | Change backdrop-filter and -webkit-backdrop-filter together. On a light page, tint the thickened state dark (background: rgba(43,42,38,.55)) or switch the links to a dark color. A bigger gap between the two states (8 px → 30 px) makes the change more visible. |
Transition duration (CSS — transition: backdrop-filter .4s, background .4s) |
0.4 s | 0.2 s for an almost immediate change, 0.8 s for a slow thickening. Add border-bottom-color to the list so the rule follows at the same pace. |
Bar height (CSS — height: 38px, margin-bottom: -38px) |
38 px | Always keep the bottom margin equal to minus the height: 46 px tall, margin-bottom: -46px. Otherwise the bar pushes the content down, and nothing passes under the glass at the start. |
Links, hover and active page (CSS — .lg-nav-items span) |
8.8 px, 80% white, pure white on hover | To get the style of Glass Navigation (fx-0303): padding: 6px 12px; border-radius: 8px, hover background: rgba(255,255,255,.1), current page as a pill background: rgba(139,92,246,.3); border: 1px solid rgba(139,92,246,.4). Turn the spans into a elements for the keyboard. |
“Scroll me” hint (HTML + CSS — .lg-navbar-scroll-hint) |
8 px, 40% white, blinks every 2 s, scrolls with the content | It only makes sense in the small demo area: delete the tag and the @keyframes lgScrollHintPulse on a real site. |
FAQ
position: sticky; top: 0 (or fixed) with a z-index above the content, keep both CSS states, and replace the script with: var n = document.querySelector('.lg-frosted-nav'); addEventListener('scroll', function () { n.classList.toggle('scrolled', scrollY > 10) }, { passive: true });. Remove data-navbar-scene and the area's inline overflow: auto, otherwise it would scroll on its own. Measured in Chromium and Firefox on a 3,000 px page: after 600 px of wheel, bar at 0 px from the top, .scrolled class set, blur at 24 px. For the content to pass under the bar from the top of the page, use a negative bottom margin (as here) or a fixed header with an opening photo that reaches the top.position: absolute bar inside the scrolling area would be placed relative to that area's content, not to its visible window: it would scroll with the text. Measured: at 60 px of scrolling, an absolute bar sits 60 px higher, out of the area — the thickened glass, the whole point of the effect, would only show between 10 and 38 px. position: sticky keeps it stuck to the top of the area, and the 38 px negative margin lets it float over the content. If you change the bar's height, change the margin with it.<button>s (reachable by keyboard) that take a 10% white fill on hover, and an active entry as a purple pill — but it reacts to nothing but hover, and its blur has no -webkit- form for Safari before 18. Frosted Glass Navbar makes the same glass bar, full width, and adds what matters on a real page: glass that thickens when content passes underneath. This page covers both: to get fx-0303's pills and hover, see “Links, hover and active page” in the table; the scenes on this page use them.