18 / 24
All 24 in 24 CSS Liquid Glass Navbars
- 01iOS 26 Floating Tab Bar
- 02macOS Tahoe Menu Bar
- 03Safari Toolbar (SVG Refraction)
- 04visionOS Glass Navbar
- 05Bottom Accessory / Mini-Player Bar
- 06Dynamic Island Navigation
- 07Tailwind Liquid Glass Navbar
- 08React Responsive Navbar (Burger)
- 09Next.js Sticky Glass Header
- 10Framer-Motion Spring Menu
- 11CSS Variables Theme Generator
- 12Bootstrap 5 Glass Navbar (Fluid)
- 13Scroll Opacity Sticky Navbar
- 14Inner-Glow Hover Links
- 15Cursor-Warp Interactive Nav
- 16Animated Gradient Blobs Navbar
- 17Sliding Capsule Active Tab
- 18Reflective Glass Sidebar
- 19Firefox Fallback Navbar
- 20High-Contrast Accessible Navbar
- 21Light / Dark Mode Navbar
- 22Performance-Tuned Navbar
- 23Minimalist Dual-Border Header
- 24Responsive Sidebar to Bottom Tabs
Reflective Glass Sidebar
A vertical glass sidebar that dynamically reflects the colour of whatever section is in view — scroll the content and the sidebar's tint, glow and active accent shift to match the adjacent panel, as if the glass were picking up the light of the room behind it.
Build interactive UI like this from scratch: 20 HTML, CSS & JavaScript projects
ZTM · JavaScript Web Projects · 30‑day money-back guarantee · Affiliate link
The code
<section class="nb-18" aria-label="Reflective glass sidebar demo">
<nav class="nb-18__side" id="nb-18-side" aria-label="Primary" style="--reflect:#5b8cff">
<span class="nb-18__glow" aria-hidden="true"></span>
<span class="nb-18__logo" aria-hidden="true">◈</span>
<a class="nb-18__item" href="#nb-18" aria-current="page" aria-label="Home">⌂</a>
<a class="nb-18__item" href="#nb-18" aria-label="Discover">◎</a>
<a class="nb-18__item" href="#nb-18" aria-label="Media">▤</a>
<a class="nb-18__item" href="#nb-18" aria-label="Settings">⚙</a>
</nav>
<div class="nb-18__scroll" id="nb-18-scroll">
<div class="nb-18__panel" data-color="#5b8cff" style="--c:#5b8cff"><h3>Ocean</h3></div>
<div class="nb-18__panel" data-color="#ff5ea8" style="--c:#ff5ea8"><h3>Bloom</h3></div>
<div class="nb-18__panel" data-color="#38e8c6" style="--c:#38e8c6"><h3>Mint</h3></div>
<div class="nb-18__panel" data-color="#ffb03a" style="--c:#ffb03a"><h3>Amber</h3></div>
</div>
</section><section class="nb-18" aria-label="Reflective glass sidebar demo">
<nav class="nb-18__side" id="nb-18-side" aria-label="Primary" style="--reflect:#5b8cff">
<span class="nb-18__glow" aria-hidden="true"></span>
<span class="nb-18__logo" aria-hidden="true">◈</span>
<a class="nb-18__item" href="#nb-18" aria-current="page" aria-label="Home">⌂</a>
<a class="nb-18__item" href="#nb-18" aria-label="Discover">◎</a>
<a class="nb-18__item" href="#nb-18" aria-label="Media">▤</a>
<a class="nb-18__item" href="#nb-18" aria-label="Settings">⚙</a>
</nav>
<div class="nb-18__scroll" id="nb-18-scroll">
<div class="nb-18__panel" data-color="#5b8cff" style="--c:#5b8cff"><h3>Ocean</h3></div>
<div class="nb-18__panel" data-color="#ff5ea8" style="--c:#ff5ea8"><h3>Bloom</h3></div>
<div class="nb-18__panel" data-color="#38e8c6" style="--c:#38e8c6"><h3>Mint</h3></div>
<div class="nb-18__panel" data-color="#ffb03a" style="--c:#ffb03a"><h3>Amber</h3></div>
</div>
</section>.nb-18,
.nb-18 *,
.nb-18 *::before,
.nb-18 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.nb-18 {
position: relative;
display: flex;
width: 100%;
min-height: 100vh;
font-family: 'Segoe UI',system-ui,sans-serif;
background: #0a0c14;
overflow: hidden;
}
.nb-18__side {
position: relative;
z-index: 5;
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
width: 74px;
padding: 18px 0;
overflow: hidden;
color: #fff;
background: color-mix(in oklab,var(--reflect) 16%,rgba(255,255,255,.06));
border-right: 1px solid color-mix(in oklab,var(--reflect) 40%,rgba(255,255,255,.14));
box-shadow: inset 0 1px 0 rgba(255,255,255,.3);
backdrop-filter: blur(24px) saturate(180%);
-webkit-backdrop-filter: blur(24px) saturate(180%);
transition: background .5s,border-color .5s;
}
.nb-18__glow {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 120px;
background: radial-gradient(60px 120px at 50% 0,color-mix(in oklab,var(--reflect) 80%,transparent),transparent 70%);
opacity: .6;
transition: background .5s;
pointer-events: none;
}
.nb-18__logo {
position: relative;
z-index: 1;
font-size: 22px;
margin-bottom: 8px;
color: color-mix(in oklab,var(--reflect) 60%,white);
}
.nb-18__item {
position: relative;
z-index: 1;
width: 46px;
height: 46px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 14px;
text-decoration: none;
color: rgba(255,255,255,.8);
font-size: 19px;
transition: background .3s,color .3s;
}
.nb-18__item:hover {
color: #fff;
background: rgba(255,255,255,.12);
}
.nb-18__item[aria-current] {
color: #fff;
background: color-mix(in oklab,var(--reflect) 55%,transparent);
box-shadow: 0 0 18px -2px color-mix(in oklab,var(--reflect) 70%,transparent);
}
.nb-18__item:focus-visible {
outline: 2px solid #fff;
outline-offset: 2px;
}
.nb-18__scroll {
flex: 1;
height: 100vh;
overflow-y: auto;
scroll-snap-type: y mandatory;
}
.nb-18__panel {
height: 100vh;
min-height: 520px;
scroll-snap-align: start;
display: grid;
place-items: center;
background: radial-gradient(circle at 50% 40%,color-mix(in oklab,var(--c) 55%,#0a0c14),#0a0c14 75%);
}
.nb-18__panel h3 {
color: #fff;
font-size: 32px;
font-weight: 800;
letter-spacing: -.02em;
text-shadow: 0 2px 20px rgba(0,0,0,.4);
}
@media (prefers-reduced-motion: reduce) {
.nb-18__side,
.nb-18__glow {
transition: none;
}
}.nb-18,
.nb-18 *,
.nb-18 *::before,
.nb-18 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.nb-18 {
position: relative;
display: flex;
width: 100%;
min-height: 100vh;
font-family: 'Segoe UI',system-ui,sans-serif;
background: #0a0c14;
overflow: hidden;
}
.nb-18__side {
position: relative;
z-index: 5;
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
width: 74px;
padding: 18px 0;
overflow: hidden;
color: #fff;
background: color-mix(in oklab,var(--reflect) 16%,rgba(255,255,255,.06));
border-right: 1px solid color-mix(in oklab,var(--reflect) 40%,rgba(255,255,255,.14));
box-shadow: inset 0 1px 0 rgba(255,255,255,.3);
backdrop-filter: blur(24px) saturate(180%);
-webkit-backdrop-filter: blur(24px) saturate(180%);
transition: background .5s,border-color .5s;
}
.nb-18__glow {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 120px;
background: radial-gradient(60px 120px at 50% 0,color-mix(in oklab,var(--reflect) 80%,transparent),transparent 70%);
opacity: .6;
transition: background .5s;
pointer-events: none;
}
.nb-18__logo {
position: relative;
z-index: 1;
font-size: 22px;
margin-bottom: 8px;
color: color-mix(in oklab,var(--reflect) 60%,white);
}
.nb-18__item {
position: relative;
z-index: 1;
width: 46px;
height: 46px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 14px;
text-decoration: none;
color: rgba(255,255,255,.8);
font-size: 19px;
transition: background .3s,color .3s;
}
.nb-18__item:hover {
color: #fff;
background: rgba(255,255,255,.12);
}
.nb-18__item[aria-current] {
color: #fff;
background: color-mix(in oklab,var(--reflect) 55%,transparent);
box-shadow: 0 0 18px -2px color-mix(in oklab,var(--reflect) 70%,transparent);
}
.nb-18__item:focus-visible {
outline: 2px solid #fff;
outline-offset: 2px;
}
.nb-18__scroll {
flex: 1;
height: 100vh;
overflow-y: auto;
scroll-snap-type: y mandatory;
}
.nb-18__panel {
height: 100vh;
min-height: 520px;
scroll-snap-align: start;
display: grid;
place-items: center;
background: radial-gradient(circle at 50% 40%,color-mix(in oklab,var(--c) 55%,#0a0c14),#0a0c14 75%);
}
.nb-18__panel h3 {
color: #fff;
font-size: 32px;
font-weight: 800;
letter-spacing: -.02em;
text-shadow: 0 2px 20px rgba(0,0,0,.4);
}
@media (prefers-reduced-motion: reduce) {
.nb-18__side,
.nb-18__glow {
transition: none;
}
}(() => {
const side = document.querySelector('#nb-18-side');
const scroll = document.querySelector('#nb-18-scroll');
if (!side || !scroll) return;
const io = new IntersectionObserver(entries => {
entries.forEach(e => { if (e.isIntersecting) side.style.setProperty('--reflect', e.target.dataset.color); });
}, { root: scroll, threshold: .6 });
scroll.querySelectorAll('.nb-18__panel').forEach(p => io.observe(p));
})();(() => {
const side = document.querySelector('#nb-18-side');
const scroll = document.querySelector('#nb-18-scroll');
if (!side || !scroll) return;
const io = new IntersectionObserver(entries => {
entries.forEach(e => { if (e.isIntersecting) side.style.setProperty('--reflect', e.target.dataset.color); });
}, { root: scroll, threshold: .6 });
scroll.querySelectorAll('.nb-18__panel').forEach(p => io.observe(p));
})();Here's a working CSS Liquid Glass Navbar from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Reflective Glass Sidebar
Source: https://codefronts.com/navigation/css-liquid-glass-navbars/reflective-glass-sidebar/
A vertical glass sidebar that dynamically reflects the colour of whatever section is in view — scroll the content and the sidebar's tint, glow and active accent shift to match the adjacent panel, as if the glass were picking up the light of the room behind it.
## HTML
```html
<section class="nb-18" aria-label="Reflective glass sidebar demo">
<nav class="nb-18__side" id="nb-18-side" aria-label="Primary" style="--reflect:#5b8cff">
<span class="nb-18__glow" aria-hidden="true"></span>
<span class="nb-18__logo" aria-hidden="true">◈</span>
<a class="nb-18__item" href="#nb-18" aria-current="page" aria-label="Home">⌂</a>
<a class="nb-18__item" href="#nb-18" aria-label="Discover">◎</a>
<a class="nb-18__item" href="#nb-18" aria-label="Media">▤</a>
<a class="nb-18__item" href="#nb-18" aria-label="Settings">⚙</a>
</nav>
<div class="nb-18__scroll" id="nb-18-scroll">
<div class="nb-18__panel" data-color="#5b8cff" style="--c:#5b8cff"><h3>Ocean</h3></div>
<div class="nb-18__panel" data-color="#ff5ea8" style="--c:#ff5ea8"><h3>Bloom</h3></div>
<div class="nb-18__panel" data-color="#38e8c6" style="--c:#38e8c6"><h3>Mint</h3></div>
<div class="nb-18__panel" data-color="#ffb03a" style="--c:#ffb03a"><h3>Amber</h3></div>
</div>
</section>
```
## CSS
```css
.nb-18,
.nb-18 *,
.nb-18 *::before,
.nb-18 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.nb-18 {
position: relative;
display: flex;
width: 100%;
min-height: 100vh;
font-family: 'Segoe UI',system-ui,sans-serif;
background: #0a0c14;
overflow: hidden;
}
.nb-18__side {
position: relative;
z-index: 5;
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
width: 74px;
padding: 18px 0;
overflow: hidden;
color: #fff;
background: color-mix(in oklab,var(--reflect) 16%,rgba(255,255,255,.06));
border-right: 1px solid color-mix(in oklab,var(--reflect) 40%,rgba(255,255,255,.14));
box-shadow: inset 0 1px 0 rgba(255,255,255,.3);
backdrop-filter: blur(24px) saturate(180%);
-webkit-backdrop-filter: blur(24px) saturate(180%);
transition: background .5s,border-color .5s;
}
.nb-18__glow {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 120px;
background: radial-gradient(60px 120px at 50% 0,color-mix(in oklab,var(--reflect) 80%,transparent),transparent 70%);
opacity: .6;
transition: background .5s;
pointer-events: none;
}
.nb-18__logo {
position: relative;
z-index: 1;
font-size: 22px;
margin-bottom: 8px;
color: color-mix(in oklab,var(--reflect) 60%,white);
}
.nb-18__item {
position: relative;
z-index: 1;
width: 46px;
height: 46px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 14px;
text-decoration: none;
color: rgba(255,255,255,.8);
font-size: 19px;
transition: background .3s,color .3s;
}
.nb-18__item:hover {
color: #fff;
background: rgba(255,255,255,.12);
}
.nb-18__item[aria-current] {
color: #fff;
background: color-mix(in oklab,var(--reflect) 55%,transparent);
box-shadow: 0 0 18px -2px color-mix(in oklab,var(--reflect) 70%,transparent);
}
.nb-18__item:focus-visible {
outline: 2px solid #fff;
outline-offset: 2px;
}
.nb-18__scroll {
flex: 1;
height: 100vh;
overflow-y: auto;
scroll-snap-type: y mandatory;
}
.nb-18__panel {
height: 100vh;
min-height: 520px;
scroll-snap-align: start;
display: grid;
place-items: center;
background: radial-gradient(circle at 50% 40%,color-mix(in oklab,var(--c) 55%,#0a0c14),#0a0c14 75%);
}
.nb-18__panel h3 {
color: #fff;
font-size: 32px;
font-weight: 800;
letter-spacing: -.02em;
text-shadow: 0 2px 20px rgba(0,0,0,.4);
}
@media (prefers-reduced-motion: reduce) {
.nb-18__side,
.nb-18__glow {
transition: none;
}
}
```
## JavaScript
```js
(() => {
const side = document.querySelector('#nb-18-side');
const scroll = document.querySelector('#nb-18-scroll');
if (!side || !scroll) return;
const io = new IntersectionObserver(entries => {
entries.forEach(e => { if (e.isIntersecting) side.style.setProperty('--reflect', e.target.dataset.color); });
}, { root: scroll, threshold: .6 });
scroll.querySelectorAll('.nb-18__panel').forEach(p => io.observe(p));
})();
```Here's a working CSS Liquid Glass Navbar from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Reflective Glass Sidebar
Source: https://codefronts.com/navigation/css-liquid-glass-navbars/reflective-glass-sidebar/
A vertical glass sidebar that dynamically reflects the colour of whatever section is in view — scroll the content and the sidebar's tint, glow and active accent shift to match the adjacent panel, as if the glass were picking up the light of the room behind it.
## HTML
```html
<section class="nb-18" aria-label="Reflective glass sidebar demo">
<nav class="nb-18__side" id="nb-18-side" aria-label="Primary" style="--reflect:#5b8cff">
<span class="nb-18__glow" aria-hidden="true"></span>
<span class="nb-18__logo" aria-hidden="true">◈</span>
<a class="nb-18__item" href="#nb-18" aria-current="page" aria-label="Home">⌂</a>
<a class="nb-18__item" href="#nb-18" aria-label="Discover">◎</a>
<a class="nb-18__item" href="#nb-18" aria-label="Media">▤</a>
<a class="nb-18__item" href="#nb-18" aria-label="Settings">⚙</a>
</nav>
<div class="nb-18__scroll" id="nb-18-scroll">
<div class="nb-18__panel" data-color="#5b8cff" style="--c:#5b8cff"><h3>Ocean</h3></div>
<div class="nb-18__panel" data-color="#ff5ea8" style="--c:#ff5ea8"><h3>Bloom</h3></div>
<div class="nb-18__panel" data-color="#38e8c6" style="--c:#38e8c6"><h3>Mint</h3></div>
<div class="nb-18__panel" data-color="#ffb03a" style="--c:#ffb03a"><h3>Amber</h3></div>
</div>
</section>
```
## CSS
```css
.nb-18,
.nb-18 *,
.nb-18 *::before,
.nb-18 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.nb-18 {
position: relative;
display: flex;
width: 100%;
min-height: 100vh;
font-family: 'Segoe UI',system-ui,sans-serif;
background: #0a0c14;
overflow: hidden;
}
.nb-18__side {
position: relative;
z-index: 5;
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
width: 74px;
padding: 18px 0;
overflow: hidden;
color: #fff;
background: color-mix(in oklab,var(--reflect) 16%,rgba(255,255,255,.06));
border-right: 1px solid color-mix(in oklab,var(--reflect) 40%,rgba(255,255,255,.14));
box-shadow: inset 0 1px 0 rgba(255,255,255,.3);
backdrop-filter: blur(24px) saturate(180%);
-webkit-backdrop-filter: blur(24px) saturate(180%);
transition: background .5s,border-color .5s;
}
.nb-18__glow {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 120px;
background: radial-gradient(60px 120px at 50% 0,color-mix(in oklab,var(--reflect) 80%,transparent),transparent 70%);
opacity: .6;
transition: background .5s;
pointer-events: none;
}
.nb-18__logo {
position: relative;
z-index: 1;
font-size: 22px;
margin-bottom: 8px;
color: color-mix(in oklab,var(--reflect) 60%,white);
}
.nb-18__item {
position: relative;
z-index: 1;
width: 46px;
height: 46px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 14px;
text-decoration: none;
color: rgba(255,255,255,.8);
font-size: 19px;
transition: background .3s,color .3s;
}
.nb-18__item:hover {
color: #fff;
background: rgba(255,255,255,.12);
}
.nb-18__item[aria-current] {
color: #fff;
background: color-mix(in oklab,var(--reflect) 55%,transparent);
box-shadow: 0 0 18px -2px color-mix(in oklab,var(--reflect) 70%,transparent);
}
.nb-18__item:focus-visible {
outline: 2px solid #fff;
outline-offset: 2px;
}
.nb-18__scroll {
flex: 1;
height: 100vh;
overflow-y: auto;
scroll-snap-type: y mandatory;
}
.nb-18__panel {
height: 100vh;
min-height: 520px;
scroll-snap-align: start;
display: grid;
place-items: center;
background: radial-gradient(circle at 50% 40%,color-mix(in oklab,var(--c) 55%,#0a0c14),#0a0c14 75%);
}
.nb-18__panel h3 {
color: #fff;
font-size: 32px;
font-weight: 800;
letter-spacing: -.02em;
text-shadow: 0 2px 20px rgba(0,0,0,.4);
}
@media (prefers-reduced-motion: reduce) {
.nb-18__side,
.nb-18__glow {
transition: none;
}
}
```
## JavaScript
```js
(() => {
const side = document.querySelector('#nb-18-side');
const scroll = document.querySelector('#nb-18-scroll');
if (!side || !scroll) return;
const io = new IntersectionObserver(entries => {
entries.forEach(e => { if (e.isIntersecting) side.style.setProperty('--reflect', e.target.dataset.color); });
}, { root: scroll, threshold: .6 });
scroll.querySelectorAll('.nb-18__panel').forEach(p => io.observe(p));
})();
```How this works
Each content section carries a data-colour. An IntersectionObserver watches which section is centred and writes its colour to a --reflect custom property on the sidebar. CSS uses --reflect in a subtle tint layer, the icon glow and the active-item accent, all transitioning smoothly — so the glass appears to sample the adjacent content's colour.
The sidebar is a real nav landmark; items are links with aria-current + focus rings; the reflection layer is decorative and aria-hidden.
Make it yours
- Feed real dominant colours (e.g. from images) into --reflect.
- Increase reflection strength via the tint layer alpha.
- Add a soft moving highlight that also tracks --reflect.
Gotchas — read before shipping
- Transition the custom property with @property (typed color) for a smooth cross-fade.
- Use IntersectionObserver, not scroll math, for accuracy + performance.
- Keep text contrast valid across every reflected colour.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.2+ | 113+ | 111+ |
Floor set by color-mix(), backdrop-filter as this demo is written. The core technique itself goes back further — Chrome 76+.
Reflection is enhancement; without JS the sidebar shows its default tint and stays fully usable.