19 / 20
All 20 in 20 Tailwind Liquid Glass Effects
- 01Tailwind Liquid Glass Card with Hover Refraction
- 02Tailwind Liquid Glass Button with Glossy Highlight
- 03Tailwind Liquid Glass Navigation Bar
- 04Tailwind Liquid Gooey Toggle Switch
- 05Tailwind Liquid Glass Modal Dialog
- 06Tailwind Liquid Glass Theme Switcher
- 07Tailwind Liquid Glass Input Form Fields
- 08Tailwind SVG Displacement Liquid Glass Background
- 09Tailwind Liquid Glass Dropdown Menu
- 10Tailwind Liquid Glass Pricing Table
- 11Tailwind Liquid Glass Tooltip Popover
- 12Tailwind Liquid Glass Audio Player UI
- 13Tailwind Liquid Glass Range Slider
- 14Tailwind Liquid Glass Toast Notification
- 15Tailwind Liquid Glass Sidebar Navigation
- 16Tailwind Liquid Glass Accordion
- 17Tailwind Liquid Glass Login Card
- 18Tailwind Liquid Glass Floating Action Button
- 19Tailwind Liquid Glass Loading Skeleton
- 20Tailwind Liquid Glass Multi-Step Form Wizard
Tailwind Liquid Glass Loading Skeleton
A profile-card skeleton where the placeholders are glass rather than grey. One card-wide shimmer sweeps across all of them — a single animated layer instead of one per block, which is both cheaper and the more convincing illusion of light gliding over a surface.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="lgt-19 grid w-full min-h-svh place-items-center p-15 font-lgt19-sans bg-[image:url('https://picsum.photos/seed/lgskel/1100/800')] bg-cover bg-center bg-fixed" aria-label="Tailwind liquid glass loading skeleton">
<div aria-busy="true" class="relative isolate w-[min(360px,92vw)] overflow-hidden rounded-[24px] border border-white/24 bg-white/9 p-6.5 backdrop-blur-[20px] backdrop-saturate-160 shadow-[inset_0_1px_0_rgba(255,255,255,.35),0_30px_60px_-30px_rgba(0,0,0,.6)] after:pointer-events-none after:absolute after:inset-0 after:z-2 after:animate-lgt19-sweep after:[background:linear-gradient(105deg,transparent_30%,rgba(255,255,255,.28)_48%,transparent_66%)] motion-reduce:after:animate-none motion-reduce:after:opacity-15">
<span class="sr-only">Loading profile…</span>
<div class="mb-5.5 flex items-center gap-4">
<div class="size-15 flex-none rounded-full border border-white/15 bg-white/16"></div>
<div class="flex flex-1 flex-col gap-2.25">
<div class="h-4 w-[65%] rounded-lg border border-white/15 bg-white/16"></div>
<div class="h-3 w-2/5 rounded-lg border border-white/15 bg-white/16"></div>
</div>
</div>
<div class="mb-3 h-3 w-full rounded-md border border-white/15 bg-white/16"></div>
<div class="mb-3 h-3 w-full rounded-md border border-white/15 bg-white/16"></div>
<div class="mb-3 h-3 w-[55%] rounded-md border border-white/15 bg-white/16"></div>
<div class="mt-4.5 flex items-center justify-between">
<div class="h-7 w-21 rounded-full border border-white/15 bg-white/16"></div>
<div class="h-10 w-27.5 rounded-xl border border-white/15 bg-white/16"></div>
</div>
</div>
</section><section class="lgt-19 grid w-full min-h-svh place-items-center p-15 font-lgt19-sans bg-[image:url('https://picsum.photos/seed/lgskel/1100/800')] bg-cover bg-center bg-fixed" aria-label="Tailwind liquid glass loading skeleton">
<div aria-busy="true" class="relative isolate w-[min(360px,92vw)] overflow-hidden rounded-[24px] border border-white/24 bg-white/9 p-6.5 backdrop-blur-[20px] backdrop-saturate-160 shadow-[inset_0_1px_0_rgba(255,255,255,.35),0_30px_60px_-30px_rgba(0,0,0,.6)] after:pointer-events-none after:absolute after:inset-0 after:z-2 after:animate-lgt19-sweep after:[background:linear-gradient(105deg,transparent_30%,rgba(255,255,255,.28)_48%,transparent_66%)] motion-reduce:after:animate-none motion-reduce:after:opacity-15">
<span class="sr-only">Loading profile…</span>
<div class="mb-5.5 flex items-center gap-4">
<div class="size-15 flex-none rounded-full border border-white/15 bg-white/16"></div>
<div class="flex flex-1 flex-col gap-2.25">
<div class="h-4 w-[65%] rounded-lg border border-white/15 bg-white/16"></div>
<div class="h-3 w-2/5 rounded-lg border border-white/15 bg-white/16"></div>
</div>
</div>
<div class="mb-3 h-3 w-full rounded-md border border-white/15 bg-white/16"></div>
<div class="mb-3 h-3 w-full rounded-md border border-white/15 bg-white/16"></div>
<div class="mb-3 h-3 w-[55%] rounded-md border border-white/15 bg-white/16"></div>
<div class="mt-4.5 flex items-center justify-between">
<div class="h-7 w-21 rounded-full border border-white/15 bg-white/16"></div>
<div class="h-10 w-27.5 rounded-xl border border-white/15 bg-white/16"></div>
</div>
</div>
</section>@import "tailwindcss";
@theme {
--font-lgt19-sans: 'Manrope', system-ui, sans-serif;
--animate-lgt19-sweep: lgt-19-sweep 1.6s ease-in-out infinite;
}
/* The hold from 60% to 100% is what makes the sweep breathe instead of strobe. */
@keyframes lgt-19-sweep {
0% {
transform: translateX(-100%);
}
60%,
100% {
transform: translateX(100%);
}
}@import "tailwindcss";
@theme {
--font-lgt19-sans: 'Manrope', system-ui, sans-serif;
--animate-lgt19-sweep: lgt-19-sweep 1.6s ease-in-out infinite;
}
/* The hold from 60% to 100% is what makes the sweep breathe instead of strobe. */
@keyframes lgt-19-sweep {
0% {
transform: translateX(-100%);
}
60%,
100% {
transform: translateX(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-lgt19-sans: 'Manrope', system-ui, sans-serif;
--animate-lgt19-sweep: lgt-19-sweep 1.6s ease-in-out infinite;
}
/* The hold from 60% to 100% is what makes the sweep breathe instead of strobe. */
@keyframes lgt-19-sweep {
0% {
transform: translateX(-100%);
}
60%,
100% {
transform: translateX(100%);
}
}@import "tailwindcss";
@theme {
--font-lgt19-sans: 'Manrope', system-ui, sans-serif;
--animate-lgt19-sweep: lgt-19-sweep 1.6s ease-in-out infinite;
}
/* The hold from 60% to 100% is what makes the sweep breathe instead of strobe. */
@keyframes lgt-19-sweep {
0% {
transform: translateX(-100%);
}
60%,
100% {
transform: translateX(100%);
}
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;600;800&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;600;800&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- A single card-wide
after:shimmer replaces per-placeholder animations — cheaper to paint and a better illusion. - Every placeholder shares one glass class string, so the skeleton has the same material vocabulary as the rest of the collection.
- Placeholder dimensions mirror the real card, which is the whole mechanism behind a zero-CLS swap.
aria-busyand ansr-onlyline give screen-reader users the information the shimmer gives everyone else.- Under
motion-reducethe sweep freezes at a faint 15% opacity rather than vanishing, so the loading cue survives.
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 Liquid Glass Loading Skeleton
Source: https://codefronts.com/design-styles/tailwind-liquid-glass-effects/tailwind-liquid-glass-loading-skeleton/
A profile-card skeleton where the placeholders are glass rather than grey. One card-wide shimmer sweeps across all of them — a single animated layer instead of one per block, which is both cheaper and the more convincing illusion of light gliding over a surface.
## HTML
```html
<section class="lgt-19 grid w-full min-h-svh place-items-center p-15 font-lgt19-sans bg-[image:url('https://picsum.photos/seed/lgskel/1100/800')] bg-cover bg-center bg-fixed" aria-label="Tailwind liquid glass loading skeleton">
<div aria-busy="true" class="relative isolate w-[min(360px,92vw)] overflow-hidden rounded-[24px] border border-white/24 bg-white/9 p-6.5 backdrop-blur-[20px] backdrop-saturate-160 shadow-[inset_0_1px_0_rgba(255,255,255,.35),0_30px_60px_-30px_rgba(0,0,0,.6)] after:pointer-events-none after:absolute after:inset-0 after:z-2 after:animate-lgt19-sweep after:[background:linear-gradient(105deg,transparent_30%,rgba(255,255,255,.28)_48%,transparent_66%)] motion-reduce:after:animate-none motion-reduce:after:opacity-15">
<span class="sr-only">Loading profile…</span>
<div class="mb-5.5 flex items-center gap-4">
<div class="size-15 flex-none rounded-full border border-white/15 bg-white/16"></div>
<div class="flex flex-1 flex-col gap-2.25">
<div class="h-4 w-[65%] rounded-lg border border-white/15 bg-white/16"></div>
<div class="h-3 w-2/5 rounded-lg border border-white/15 bg-white/16"></div>
</div>
</div>
<div class="mb-3 h-3 w-full rounded-md border border-white/15 bg-white/16"></div>
<div class="mb-3 h-3 w-full rounded-md border border-white/15 bg-white/16"></div>
<div class="mb-3 h-3 w-[55%] rounded-md border border-white/15 bg-white/16"></div>
<div class="mt-4.5 flex items-center justify-between">
<div class="h-7 w-21 rounded-full border border-white/15 bg-white/16"></div>
<div class="h-10 w-27.5 rounded-xl border border-white/15 bg-white/16"></div>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--font-lgt19-sans: 'Manrope', system-ui, sans-serif;
--animate-lgt19-sweep: lgt-19-sweep 1.6s ease-in-out infinite;
}
/* The hold from 60% to 100% is what makes the sweep breathe instead of strobe. */
@keyframes lgt-19-sweep {
0% {
transform: translateX(-100%);
}
60%,
100% {
transform: translateX(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 Liquid Glass Loading Skeleton
Source: https://codefronts.com/design-styles/tailwind-liquid-glass-effects/tailwind-liquid-glass-loading-skeleton/
A profile-card skeleton where the placeholders are glass rather than grey. One card-wide shimmer sweeps across all of them — a single animated layer instead of one per block, which is both cheaper and the more convincing illusion of light gliding over a surface.
## HTML
```html
<section class="lgt-19 grid w-full min-h-svh place-items-center p-15 font-lgt19-sans bg-[image:url('https://picsum.photos/seed/lgskel/1100/800')] bg-cover bg-center bg-fixed" aria-label="Tailwind liquid glass loading skeleton">
<div aria-busy="true" class="relative isolate w-[min(360px,92vw)] overflow-hidden rounded-[24px] border border-white/24 bg-white/9 p-6.5 backdrop-blur-[20px] backdrop-saturate-160 shadow-[inset_0_1px_0_rgba(255,255,255,.35),0_30px_60px_-30px_rgba(0,0,0,.6)] after:pointer-events-none after:absolute after:inset-0 after:z-2 after:animate-lgt19-sweep after:[background:linear-gradient(105deg,transparent_30%,rgba(255,255,255,.28)_48%,transparent_66%)] motion-reduce:after:animate-none motion-reduce:after:opacity-15">
<span class="sr-only">Loading profile…</span>
<div class="mb-5.5 flex items-center gap-4">
<div class="size-15 flex-none rounded-full border border-white/15 bg-white/16"></div>
<div class="flex flex-1 flex-col gap-2.25">
<div class="h-4 w-[65%] rounded-lg border border-white/15 bg-white/16"></div>
<div class="h-3 w-2/5 rounded-lg border border-white/15 bg-white/16"></div>
</div>
</div>
<div class="mb-3 h-3 w-full rounded-md border border-white/15 bg-white/16"></div>
<div class="mb-3 h-3 w-full rounded-md border border-white/15 bg-white/16"></div>
<div class="mb-3 h-3 w-[55%] rounded-md border border-white/15 bg-white/16"></div>
<div class="mt-4.5 flex items-center justify-between">
<div class="h-7 w-21 rounded-full border border-white/15 bg-white/16"></div>
<div class="h-10 w-27.5 rounded-xl border border-white/15 bg-white/16"></div>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--font-lgt19-sans: 'Manrope', system-ui, sans-serif;
--animate-lgt19-sweep: lgt-19-sweep 1.6s ease-in-out infinite;
}
/* The hold from 60% to 100% is what makes the sweep breathe instead of strobe. */
@keyframes lgt-19-sweep {
0% {
transform: translateX(-100%);
}
60%,
100% {
transform: translateX(100%);
}
}
```How this works
One sweep, not twelve. The naive skeleton animates a gradient on every placeholder, which is a dozen animated layers for one loading state. Here a single after: layer spans the card and translates across everything:
after:absolute after:inset-0 after:z-2 after:pointer-events-none
after:[background:linear-gradient(105deg,transparent_30%,rgba(255,255,255,.28)_48%,transparent_66%)]
after:animate-lgt19-sweepThe keyframe holds at the end. 0% to 60% travels, 60% to 100% rests off-screen. Without that pause the sweep reads as a strobe; with it, it reads as light passing periodically over glass.
Zero CLS is the actual product requirement. Every placeholder matches the dimensions of the element it stands in for, so when real content arrives nothing moves. A skeleton that shifts layout on swap has made the problem worse, not better.
Loading is announced, not just drawn. aria-busy="true" and an sr-only line tell screen-reader users what the shimmer tells everyone else — and both come off when the data lands.
Make it yours
- Copy your real card's exact box sizes into the placeholders; that is the entire technique for a CLS-free swap.
- Change the shimmer's speed and angle in the keyframe; 1.6s with a hold is calm, sub-1s starts to feel anxious.
- Tint the placeholders to your surface colour instead of white for skeletons on light backgrounds.
- Add or remove lines to mirror richer cards — the single sweep covers whatever is underneath.
- Wrap it in a component that renders skeleton or content from one prop, so the two layouts cannot drift apart.
Gotchas — read before shipping
- Mirror the final layout's dimensions or the swap causes layout shift, which is the exact metric a skeleton exists to protect.
- Use one card-wide shimmer. One animation per placeholder multiplies paint cost for a strictly worse effect.
- Announce the loading state and remove
aria-busywhen done — a region permanently marked busy is a bug that only screen-reader users experience. - The shimmer needs
pointer-events-none, or it will sit over any interactive content you reveal underneath it. overflow-hiddenon the card clips the sweep; without it the light beam escapes past the rounded corners.- Under
motion-reducekeep a faint static highlight rather than nothing — the user still needs to know something is loading.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Utilities only, no JavaScript. Where backdrop-filter is unavailable the placeholders are solid translucent shapes with the same shimmer, and the CLS behaviour is identical.