17 / 24
All 24 in 24 Tailwind Liquid Glass Navbars
- 01Tailwind iOS 26 Floating Tab Bar
- 02Tailwind macOS Tahoe Menu Bar
- 03Tailwind Safari Toolbar with SVG Refraction
- 04Tailwind visionOS Glass Navbar
- 05Tailwind Liquid Glass Bottom Accessory Bar
- 06Tailwind Dynamic Island Morphing Nav
- 07Tailwind Liquid Glass Navbar (Canonical)
- 08Tailwind Responsive Glass Navbar with Burger
- 09Tailwind Next.js Sticky Glass Header
- 10Tailwind Spring Glide Menu (Framer Motion Port)
- 11Tailwind CSS Variables Glass Theme Generator
- 12Tailwind Glass Override for Bootstrap 5 Navbars
- 13Tailwind Scroll-Opacity Sticky Navbar
- 14Tailwind Inner-Glow Glass Nav Links
- 15Tailwind Cursor-Lens Glass Nav (GSAP-ready)
- 16Tailwind Animated Gradient Blobs Glass Navbar
- 17Tailwind Sliding Capsule Tab Bar (group-has)
- 18Tailwind Reflective Glass Sidebar
- 19Tailwind Fallback-First Glass Navbar
- 20Tailwind High-Contrast Accessible Glass Navbar
- 21Tailwind Light / Dark Liquid Glass Navbar
- 22Tailwind Performance-Tuned Glass Navbar
- 23Tailwind Minimalist Dual-Border Glass Header
- 24Tailwind Responsive Glass Sidebar to Bottom Tabs
Tailwind Sliding Capsule Tab Bar (group-has)
A segmented glass tab bar where the capsule slides behind the selected item with no JavaScript — and the port that best justifies itself. The CSS version needs four :has() rules in a stylesheet; here the whole state table lives on the capsule as group-has-[#nbtw-17-2:checked]:translate-x-[86px], one utility per tab, readable in place.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="nbtw-17 grid place-items-center w-full min-h-svh p-5 font-nb17-sans bg-[conic-gradient(from_200deg_at_50%_50%,#00c2ff,#8e2de2,#ff6a88,#00c2ff)] bg-[length:200%_200%] animate-nb17-bg motion-reduce:animate-none" aria-label="Sliding capsule active tab demo">
<div class="group relative flex p-[7px] rounded-full isolate bg-[color-mix(in_oklab,white_14%,transparent)] border border-white/40 shadow-[inset_0_1px_0_rgba(255,255,255,.55),0_20px_44px_-24px_rgba(0,0,0,.55)] backdrop-blur-[22px] backdrop-saturate-[1.8]">
<span class="absolute inset-y-[7px] left-[7px] z-0 w-[86px] rounded-full bg-white/[0.26] border border-white/[0.55] shadow-[inset_0_1px_0_rgba(255,255,255,.7)] transition-transform duration-[450ms] ease-nb17 group-has-[#nbtw-17-2:checked]:translate-x-[86px] group-has-[#nbtw-17-3:checked]:translate-x-[172px] group-has-[#nbtw-17-4:checked]:translate-x-[258px] motion-reduce:transition-none" aria-hidden="true"></span>
<input class="peer sr-only" type="radio" name="nbtw-17" id="nbtw-17-1" checked>
<label class="relative z-[1] flex items-center justify-center w-[86px] min-h-10 rounded-full cursor-pointer text-white/85 text-[14.5px] font-semibold transition-colors duration-250 peer-checked:text-white peer-focus-visible:outline-2 peer-focus-visible:outline-white peer-focus-visible:outline-offset-2" for="nbtw-17-1">Day</label>
<input class="peer sr-only" type="radio" name="nbtw-17" id="nbtw-17-2">
<label class="relative z-[1] flex items-center justify-center w-[86px] min-h-10 rounded-full cursor-pointer text-white/85 text-[14.5px] font-semibold transition-colors duration-250 peer-checked:text-white peer-focus-visible:outline-2 peer-focus-visible:outline-white peer-focus-visible:outline-offset-2" for="nbtw-17-2">Week</label>
<input class="peer sr-only" type="radio" name="nbtw-17" id="nbtw-17-3">
<label class="relative z-[1] flex items-center justify-center w-[86px] min-h-10 rounded-full cursor-pointer text-white/85 text-[14.5px] font-semibold transition-colors duration-250 peer-checked:text-white peer-focus-visible:outline-2 peer-focus-visible:outline-white peer-focus-visible:outline-offset-2" for="nbtw-17-3">Month</label>
<input class="peer sr-only" type="radio" name="nbtw-17" id="nbtw-17-4">
<label class="relative z-[1] flex items-center justify-center w-[86px] min-h-10 rounded-full cursor-pointer text-white/85 text-[14.5px] font-semibold transition-colors duration-250 peer-checked:text-white peer-focus-visible:outline-2 peer-focus-visible:outline-white peer-focus-visible:outline-offset-2" for="nbtw-17-4">Year</label>
</div>
</section><section class="nbtw-17 grid place-items-center w-full min-h-svh p-5 font-nb17-sans bg-[conic-gradient(from_200deg_at_50%_50%,#00c2ff,#8e2de2,#ff6a88,#00c2ff)] bg-[length:200%_200%] animate-nb17-bg motion-reduce:animate-none" aria-label="Sliding capsule active tab demo">
<div class="group relative flex p-[7px] rounded-full isolate bg-[color-mix(in_oklab,white_14%,transparent)] border border-white/40 shadow-[inset_0_1px_0_rgba(255,255,255,.55),0_20px_44px_-24px_rgba(0,0,0,.55)] backdrop-blur-[22px] backdrop-saturate-[1.8]">
<span class="absolute inset-y-[7px] left-[7px] z-0 w-[86px] rounded-full bg-white/[0.26] border border-white/[0.55] shadow-[inset_0_1px_0_rgba(255,255,255,.7)] transition-transform duration-[450ms] ease-nb17 group-has-[#nbtw-17-2:checked]:translate-x-[86px] group-has-[#nbtw-17-3:checked]:translate-x-[172px] group-has-[#nbtw-17-4:checked]:translate-x-[258px] motion-reduce:transition-none" aria-hidden="true"></span>
<input class="peer sr-only" type="radio" name="nbtw-17" id="nbtw-17-1" checked>
<label class="relative z-[1] flex items-center justify-center w-[86px] min-h-10 rounded-full cursor-pointer text-white/85 text-[14.5px] font-semibold transition-colors duration-250 peer-checked:text-white peer-focus-visible:outline-2 peer-focus-visible:outline-white peer-focus-visible:outline-offset-2" for="nbtw-17-1">Day</label>
<input class="peer sr-only" type="radio" name="nbtw-17" id="nbtw-17-2">
<label class="relative z-[1] flex items-center justify-center w-[86px] min-h-10 rounded-full cursor-pointer text-white/85 text-[14.5px] font-semibold transition-colors duration-250 peer-checked:text-white peer-focus-visible:outline-2 peer-focus-visible:outline-white peer-focus-visible:outline-offset-2" for="nbtw-17-2">Week</label>
<input class="peer sr-only" type="radio" name="nbtw-17" id="nbtw-17-3">
<label class="relative z-[1] flex items-center justify-center w-[86px] min-h-10 rounded-full cursor-pointer text-white/85 text-[14.5px] font-semibold transition-colors duration-250 peer-checked:text-white peer-focus-visible:outline-2 peer-focus-visible:outline-white peer-focus-visible:outline-offset-2" for="nbtw-17-3">Month</label>
<input class="peer sr-only" type="radio" name="nbtw-17" id="nbtw-17-4">
<label class="relative z-[1] flex items-center justify-center w-[86px] min-h-10 rounded-full cursor-pointer text-white/85 text-[14.5px] font-semibold transition-colors duration-250 peer-checked:text-white peer-focus-visible:outline-2 peer-focus-visible:outline-white peer-focus-visible:outline-offset-2" for="nbtw-17-4">Year</label>
</div>
</section>@import "tailwindcss";
@theme {
--font-nb17-sans: system-ui, 'Segoe UI', sans-serif;
--ease-nb17: cubic-bezier(0.34, 1.56, 0.5, 1);
--animate-nb17-bg: nb17-bg 20s ease-in-out infinite;
}
@keyframes nb17-bg {
50% {
background-position: 100% 100%;
}
}@import "tailwindcss";
@theme {
--font-nb17-sans: system-ui, 'Segoe UI', sans-serif;
--ease-nb17: cubic-bezier(0.34, 1.56, 0.5, 1);
--animate-nb17-bg: nb17-bg 20s ease-in-out infinite;
}
@keyframes nb17-bg {
50% {
background-position: 100% 100%;
}
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--font-nb17-sans: system-ui, 'Segoe UI', sans-serif;
--ease-nb17: cubic-bezier(0.34, 1.56, 0.5, 1);
--animate-nb17-bg: nb17-bg 20s ease-in-out infinite;
}
@keyframes nb17-bg {
50% {
background-position: 100% 100%;
}
}@import "tailwindcss";
@theme {
--font-nb17-sans: system-ui, 'Segoe UI', sans-serif;
--ease-nb17: cubic-bezier(0.34, 1.56, 0.5, 1);
--animate-nb17-bg: nb17-bg 20s ease-in-out infinite;
}
@keyframes nb17-bg {
50% {
background-position: 100% 100%;
}
}What changed in the port
Which parts became utilities, which stayed CSS, and why.
- This is the port that most clearly pays for itself: four
:has()rules become four utilities on the capsule, and the whole state table is visible at once instead of living in a stylesheet. group-has-[#id:checked]is the key —groupon the bar lets the capsule ask about a sibling input's state, which no plain child selector can do.- The radios are
sr-only, nothidden, so they keep their place in the tab order and native arrow-key movement between tabs survives the port. peer-focus-visible:paints the focus ring on the visible label instead of the invisible input — the pattern that makes visually-hidden controls usable.peer-checked:text-whitereplaces the worst selector in the hand-written version, a#id:checked ~ label[for=id]chain repeated four times.- The literal offsets are the trade-off: changing the tab width means editing the width and all three translate values, which is the price of having them in one readable place.
Here's a working CSS demo 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: Tailwind Sliding Capsule Tab Bar (group-has)
Source: https://codefronts.com/navigation/tailwind-liquid-glass-navbars/tailwind-sliding-capsule-tab-bar-group-has/
A segmented glass tab bar where the capsule slides behind the selected item with no JavaScript — and the port that best justifies itself. The CSS version needs four :has() rules in a stylesheet; here the whole state table lives on the capsule as group-has-[#nbtw-17-2:checked]:translate-x-[86px], one utility per tab, readable in place.
## HTML
```html
<section class="nbtw-17 grid place-items-center w-full min-h-svh p-5 font-nb17-sans bg-[conic-gradient(from_200deg_at_50%_50%,#00c2ff,#8e2de2,#ff6a88,#00c2ff)] bg-[length:200%_200%] animate-nb17-bg motion-reduce:animate-none" aria-label="Sliding capsule active tab demo">
<div class="group relative flex p-[7px] rounded-full isolate bg-[color-mix(in_oklab,white_14%,transparent)] border border-white/40 shadow-[inset_0_1px_0_rgba(255,255,255,.55),0_20px_44px_-24px_rgba(0,0,0,.55)] backdrop-blur-[22px] backdrop-saturate-[1.8]">
<span class="absolute inset-y-[7px] left-[7px] z-0 w-[86px] rounded-full bg-white/[0.26] border border-white/[0.55] shadow-[inset_0_1px_0_rgba(255,255,255,.7)] transition-transform duration-[450ms] ease-nb17 group-has-[#nbtw-17-2:checked]:translate-x-[86px] group-has-[#nbtw-17-3:checked]:translate-x-[172px] group-has-[#nbtw-17-4:checked]:translate-x-[258px] motion-reduce:transition-none" aria-hidden="true"></span>
<input class="peer sr-only" type="radio" name="nbtw-17" id="nbtw-17-1" checked>
<label class="relative z-[1] flex items-center justify-center w-[86px] min-h-10 rounded-full cursor-pointer text-white/85 text-[14.5px] font-semibold transition-colors duration-250 peer-checked:text-white peer-focus-visible:outline-2 peer-focus-visible:outline-white peer-focus-visible:outline-offset-2" for="nbtw-17-1">Day</label>
<input class="peer sr-only" type="radio" name="nbtw-17" id="nbtw-17-2">
<label class="relative z-[1] flex items-center justify-center w-[86px] min-h-10 rounded-full cursor-pointer text-white/85 text-[14.5px] font-semibold transition-colors duration-250 peer-checked:text-white peer-focus-visible:outline-2 peer-focus-visible:outline-white peer-focus-visible:outline-offset-2" for="nbtw-17-2">Week</label>
<input class="peer sr-only" type="radio" name="nbtw-17" id="nbtw-17-3">
<label class="relative z-[1] flex items-center justify-center w-[86px] min-h-10 rounded-full cursor-pointer text-white/85 text-[14.5px] font-semibold transition-colors duration-250 peer-checked:text-white peer-focus-visible:outline-2 peer-focus-visible:outline-white peer-focus-visible:outline-offset-2" for="nbtw-17-3">Month</label>
<input class="peer sr-only" type="radio" name="nbtw-17" id="nbtw-17-4">
<label class="relative z-[1] flex items-center justify-center w-[86px] min-h-10 rounded-full cursor-pointer text-white/85 text-[14.5px] font-semibold transition-colors duration-250 peer-checked:text-white peer-focus-visible:outline-2 peer-focus-visible:outline-white peer-focus-visible:outline-offset-2" for="nbtw-17-4">Year</label>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--font-nb17-sans: system-ui, 'Segoe UI', sans-serif;
--ease-nb17: cubic-bezier(0.34, 1.56, 0.5, 1);
--animate-nb17-bg: nb17-bg 20s ease-in-out infinite;
}
@keyframes nb17-bg {
50% {
background-position: 100% 100%;
}
}
```Here's a working CSS demo 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: Tailwind Sliding Capsule Tab Bar (group-has)
Source: https://codefronts.com/navigation/tailwind-liquid-glass-navbars/tailwind-sliding-capsule-tab-bar-group-has/
A segmented glass tab bar where the capsule slides behind the selected item with no JavaScript — and the port that best justifies itself. The CSS version needs four :has() rules in a stylesheet; here the whole state table lives on the capsule as group-has-[#nbtw-17-2:checked]:translate-x-[86px], one utility per tab, readable in place.
## HTML
```html
<section class="nbtw-17 grid place-items-center w-full min-h-svh p-5 font-nb17-sans bg-[conic-gradient(from_200deg_at_50%_50%,#00c2ff,#8e2de2,#ff6a88,#00c2ff)] bg-[length:200%_200%] animate-nb17-bg motion-reduce:animate-none" aria-label="Sliding capsule active tab demo">
<div class="group relative flex p-[7px] rounded-full isolate bg-[color-mix(in_oklab,white_14%,transparent)] border border-white/40 shadow-[inset_0_1px_0_rgba(255,255,255,.55),0_20px_44px_-24px_rgba(0,0,0,.55)] backdrop-blur-[22px] backdrop-saturate-[1.8]">
<span class="absolute inset-y-[7px] left-[7px] z-0 w-[86px] rounded-full bg-white/[0.26] border border-white/[0.55] shadow-[inset_0_1px_0_rgba(255,255,255,.7)] transition-transform duration-[450ms] ease-nb17 group-has-[#nbtw-17-2:checked]:translate-x-[86px] group-has-[#nbtw-17-3:checked]:translate-x-[172px] group-has-[#nbtw-17-4:checked]:translate-x-[258px] motion-reduce:transition-none" aria-hidden="true"></span>
<input class="peer sr-only" type="radio" name="nbtw-17" id="nbtw-17-1" checked>
<label class="relative z-[1] flex items-center justify-center w-[86px] min-h-10 rounded-full cursor-pointer text-white/85 text-[14.5px] font-semibold transition-colors duration-250 peer-checked:text-white peer-focus-visible:outline-2 peer-focus-visible:outline-white peer-focus-visible:outline-offset-2" for="nbtw-17-1">Day</label>
<input class="peer sr-only" type="radio" name="nbtw-17" id="nbtw-17-2">
<label class="relative z-[1] flex items-center justify-center w-[86px] min-h-10 rounded-full cursor-pointer text-white/85 text-[14.5px] font-semibold transition-colors duration-250 peer-checked:text-white peer-focus-visible:outline-2 peer-focus-visible:outline-white peer-focus-visible:outline-offset-2" for="nbtw-17-2">Week</label>
<input class="peer sr-only" type="radio" name="nbtw-17" id="nbtw-17-3">
<label class="relative z-[1] flex items-center justify-center w-[86px] min-h-10 rounded-full cursor-pointer text-white/85 text-[14.5px] font-semibold transition-colors duration-250 peer-checked:text-white peer-focus-visible:outline-2 peer-focus-visible:outline-white peer-focus-visible:outline-offset-2" for="nbtw-17-3">Month</label>
<input class="peer sr-only" type="radio" name="nbtw-17" id="nbtw-17-4">
<label class="relative z-[1] flex items-center justify-center w-[86px] min-h-10 rounded-full cursor-pointer text-white/85 text-[14.5px] font-semibold transition-colors duration-250 peer-checked:text-white peer-focus-visible:outline-2 peer-focus-visible:outline-white peer-focus-visible:outline-offset-2" for="nbtw-17-4">Year</label>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--font-nb17-sans: system-ui, 'Segoe UI', sans-serif;
--ease-nb17: cubic-bezier(0.34, 1.56, 0.5, 1);
--animate-nb17-bg: nb17-bg 20s ease-in-out infinite;
}
@keyframes nb17-bg {
50% {
background-position: 100% 100%;
}
}
```How this works
The state table moves into the markup. Four stylesheet rules become four utilities on one element:
class="absolute inset-y-[7px] left-[7px] w-[86px] rounded-full
transition-transform duration-[450ms] ease-nb17
group-has-[#nbtw-17-2:checked]:translate-x-[86px]
group-has-[#nbtw-17-3:checked]:translate-x-[172px]
group-has-[#nbtw-17-4:checked]:translate-x-[258px]"Adding a fifth tab is now one more utility next to the others, rather than a new rule in a file somewhere else — and the whole table is visible at once, which is what makes an off-by-one in the offsets obvious.
Radios must stay focusable. sr-only clips them; hidden would remove them from the tab order and delete native arrow-key movement between options, which is the accessibility win of using radios in the first place.
The ring goes on the label. The input is invisible, so peer-focus-visible: on each label paints the ring where the user is actually looking — the sibling-variant pattern that makes visually-hidden form controls viable.
peer-checked: replaces the checked-sibling selector. The selected label brightens through a variant rather than a #id:checked ~ label[for=id] chain, which is the single ugliest selector in the hand-written version.
Make it yours
- Add tabs by appending an input/label pair and one more
group-has-[#id:checked]:translate-x-*utility; keep the offsets multiples of the tab width. - Change the capsule feel by editing
--ease-nb17— more overshoot reads springier, less reads mechanical. - Go vertical by swapping the translate axis to
translate-y-*and stacking the bar withflex-col. - Widen the tabs by changing both the label width and the capsule width plus every offset — they are one measurement in three places.
- Drop the capsule and use
peer-checked:bg-white/25on the labels for a simpler version with no offsets to maintain.
Gotchas — read before shipping
- Every offset must equal the tab width times its index. Because they are literal values, a width change means editing all of them — the honest cost of moving the table into the markup.
- Use
sr-only, neverhiddenordisplay:none, on the radios. Hidden inputs are unfocusable and you lose native arrow-key movement. group-has-[...]needsgroupon the ancestor that contains both the capsule and the inputs.- Arbitrary variant selectors are matched literally, so the ids inside
group-has-[#nbtw-17-2:checked]must be namespaced or two copies of the demo on one page will drive each other. - Keep the capsule width equal to the label width or the highlight sits visibly off-centre at one end of the row.
:has()is what makes the no-JS version possible; on older engines the tabs still work as radios, they just do not slide.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Tailwind v4's floor already includes :has() (Chrome 105+, Safari 15.4+, Firefox 121+), so the no-JS slide is inside the baseline. Older engines get a static checked highlight and a fully usable tab bar.