Tailwind v4MIT licensed

Tailwind Liquid Glass Button with Glossy Highlight

A capsule CTA in the macOS Tahoe / iOS 26 language, and a clean demonstration of what group buys you: the sweep lives on an after: layer parked off-screen, and group-hover:after:[transform:...] plus its group-focus-visible: twin drives it across. Two buttons, one shared class string, and the primary variant differs only by its gradient fill.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<section class="lgt-02 grid w-full min-h-svh place-items-center p-15 font-lgt02-sans bg-[image:url('https://picsum.photos/seed/lgbtn/1200/900')] bg-cover bg-center bg-fixed" aria-label="Tailwind liquid glass buttons">
  <div class="flex flex-wrap items-center justify-center gap-5">
    <button type="button" class="group relative isolate overflow-hidden cursor-pointer rounded-full border border-lgt02-accent/60 bg-linear-to-b from-lgt02-accent-top to-lgt02-accent px-8.5 py-4 text-base font-bold text-white backdrop-blur-[16px] backdrop-saturate-170 shadow-[inset_0_1px_0_rgba(255,255,255,.6),inset_0_-8px_18px_-10px_rgba(0,0,0,.5),0_14px_30px_-16px_rgba(0,0,0,.6)] transition-[transform,box-shadow] duration-200 hover:-translate-y-0.5 focus-visible:-translate-y-0.5 active:translate-y-0 active:scale-97 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-white after:pointer-events-none after:absolute after:inset-y-0 after:left-0 after:z-2 after:w-3/5 after:[background:linear-gradient(105deg,transparent_20%,rgba(255,255,255,.55)_50%,transparent_80%)] after:[transform:translateX(-160%)_skewX(-12deg)] after:transition-transform after:duration-700 after:ease-lgt02 group-hover:after:[transform:translateX(230%)_skewX(-12deg)] group-focus-visible:after:[transform:translateX(230%)_skewX(-12deg)] motion-reduce:transition-none motion-reduce:after:transition-none motion-reduce:group-hover:after:[transform:translateX(-160%)_skewX(-12deg)]">Get started</button>

    <button type="button" class="group relative isolate overflow-hidden cursor-pointer rounded-full border border-white/40 bg-white/14 px-8.5 py-4 text-base font-bold text-white backdrop-blur-[16px] backdrop-saturate-170 shadow-[inset_0_1px_0_rgba(255,255,255,.6),inset_0_-8px_18px_-10px_rgba(0,0,0,.5),0_14px_30px_-16px_rgba(0,0,0,.6)] transition-[transform,box-shadow] duration-200 hover:-translate-y-0.5 focus-visible:-translate-y-0.5 active:translate-y-0 active:scale-97 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-white after:pointer-events-none after:absolute after:inset-y-0 after:left-0 after:z-2 after:w-3/5 after:[background:linear-gradient(105deg,transparent_20%,rgba(255,255,255,.55)_50%,transparent_80%)] after:[transform:translateX(-160%)_skewX(-12deg)] after:transition-transform after:duration-700 after:ease-lgt02 group-hover:after:[transform:translateX(230%)_skewX(-12deg)] group-focus-visible:after:[transform:translateX(230%)_skewX(-12deg)] motion-reduce:transition-none motion-reduce:after:transition-none motion-reduce:group-hover:after:[transform:translateX(-160%)_skewX(-12deg)]">Learn more</button>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-lgt02-accent: #6a4bff;
  --color-lgt02-accent: oklch(0.62 0.2 275);
  --color-lgt02-accent-top: #8168ff;
  --color-lgt02-accent-top: oklch(0.68 0.18 275);
  --font-lgt02-sans: 'Manrope', system-ui, sans-serif;
  --ease-lgt02: cubic-bezier(0.2, 0.8, 0.2, 1);
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<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.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-lgt02-accent: #6a4bff;
  --color-lgt02-accent: oklch(0.62 0.2 275);
  --color-lgt02-accent-top: #8168ff;
  --color-lgt02-accent-top: oklch(0.68 0.18 275);
  --font-lgt02-sans: 'Manrope', system-ui, sans-serif;
  --ease-lgt02: cubic-bezier(0.2, 0.8, 0.2, 1);
}
3Fonts used by this demooptional — skip if you have your own type stack
<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.

  • The two buttons share one class string; the primary only swaps the fill and border, so the variant is a diff rather than a second rule block.
  • The specular sweep moves from a standalone ::after rule into an after: variant chain, including its own transition and its own reduced-motion pin.
  • group-hover: and group-focus-visible: are always typed as a pair — the keyboard affordance becomes a habit you can see in the markup.
  • The gradient stop that was color-mix(in oklab, var(--accent) 88%, white) becomes a second theme colour, --color-lgt02-accent-top, which is cheaper to read and mints its own from-* utility.
  • active:scale-97 is a real v4 utility — the arbitrary [transform:scale(.97)] the CSS version needed is gone.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
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 Button with Glossy Highlight
Source: https://codefronts.com/design-styles/tailwind-liquid-glass-effects/tailwind-liquid-glass-button-with-glossy-highlight/

A capsule CTA in the macOS Tahoe / iOS 26 language, and a clean demonstration of what group buys you: the sweep lives on an after: layer parked off-screen, and group-hover:after:[transform:...] plus its group-focus-visible: twin drives it across. Two buttons, one shared class string, and the primary variant differs only by its gradient fill.
## HTML
```html
<section class="lgt-02 grid w-full min-h-svh place-items-center p-15 font-lgt02-sans bg-[image:url('https://picsum.photos/seed/lgbtn/1200/900')] bg-cover bg-center bg-fixed" aria-label="Tailwind liquid glass buttons">
  <div class="flex flex-wrap items-center justify-center gap-5">
    <button type="button" class="group relative isolate overflow-hidden cursor-pointer rounded-full border border-lgt02-accent/60 bg-linear-to-b from-lgt02-accent-top to-lgt02-accent px-8.5 py-4 text-base font-bold text-white backdrop-blur-[16px] backdrop-saturate-170 shadow-[inset_0_1px_0_rgba(255,255,255,.6),inset_0_-8px_18px_-10px_rgba(0,0,0,.5),0_14px_30px_-16px_rgba(0,0,0,.6)] transition-[transform,box-shadow] duration-200 hover:-translate-y-0.5 focus-visible:-translate-y-0.5 active:translate-y-0 active:scale-97 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-white after:pointer-events-none after:absolute after:inset-y-0 after:left-0 after:z-2 after:w-3/5 after:[background:linear-gradient(105deg,transparent_20%,rgba(255,255,255,.55)_50%,transparent_80%)] after:[transform:translateX(-160%)_skewX(-12deg)] after:transition-transform after:duration-700 after:ease-lgt02 group-hover:after:[transform:translateX(230%)_skewX(-12deg)] group-focus-visible:after:[transform:translateX(230%)_skewX(-12deg)] motion-reduce:transition-none motion-reduce:after:transition-none motion-reduce:group-hover:after:[transform:translateX(-160%)_skewX(-12deg)]">Get started</button>

    <button type="button" class="group relative isolate overflow-hidden cursor-pointer rounded-full border border-white/40 bg-white/14 px-8.5 py-4 text-base font-bold text-white backdrop-blur-[16px] backdrop-saturate-170 shadow-[inset_0_1px_0_rgba(255,255,255,.6),inset_0_-8px_18px_-10px_rgba(0,0,0,.5),0_14px_30px_-16px_rgba(0,0,0,.6)] transition-[transform,box-shadow] duration-200 hover:-translate-y-0.5 focus-visible:-translate-y-0.5 active:translate-y-0 active:scale-97 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-white after:pointer-events-none after:absolute after:inset-y-0 after:left-0 after:z-2 after:w-3/5 after:[background:linear-gradient(105deg,transparent_20%,rgba(255,255,255,.55)_50%,transparent_80%)] after:[transform:translateX(-160%)_skewX(-12deg)] after:transition-transform after:duration-700 after:ease-lgt02 group-hover:after:[transform:translateX(230%)_skewX(-12deg)] group-focus-visible:after:[transform:translateX(230%)_skewX(-12deg)] motion-reduce:transition-none motion-reduce:after:transition-none motion-reduce:group-hover:after:[transform:translateX(-160%)_skewX(-12deg)]">Learn more</button>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-lgt02-accent: #6a4bff;
  --color-lgt02-accent: oklch(0.62 0.2 275);
  --color-lgt02-accent-top: #8168ff;
  --color-lgt02-accent-top: oklch(0.68 0.18 275);
  --font-lgt02-sans: 'Manrope', system-ui, sans-serif;
  --ease-lgt02: cubic-bezier(0.2, 0.8, 0.2, 1);
}
```

How this works

One class string, two buttons. The glass capsule is shared; the primary adds a gradient fill and a lighter border. That is the entire difference between the variants, and you can see it in the diff rather than in two CSS blocks:

<!-- shared -->
class="group relative isolate overflow-hidden rounded-full
       border border-white/40 bg-white/14
       backdrop-blur-[16px] backdrop-saturate-170"

<!-- primary adds -->
class="bg-linear-to-b from-lgt02-accentTop to-lgt02-accent
       border-lgt02-accent/60"

The sweep is one pseudo-element and one transform. A narrow diagonal white gradient sits at 60% width, parked left of the button, skewed. On hover it translates right across the surface over 700ms. Nothing animates layout, so the whole thing runs on the compositor.

Thickness comes from two inset shadows, not from a border-radius trick. A 1px bright inset at the top is the rim-light where the glass edge catches light; a soft dark inset at the bottom is the occlusion under it. Together they read as a solid object rather than a coloured rectangle.

group is doing real work here. The sweep and the label both need the button's hover state, and both are typed with a group-focus-visible: twin — so the keyboard path is visibly identical, not an afterthought.

Make it yours

  • Rebrand from one line: edit --color-lgt02-accent in @theme and the fill, border and focus ring all follow.
  • Slow the sweep with group-hover:after:duration-1000 for a more languid, luxury feel; speed it to 400ms for a snappier product UI.
  • Drop the gradient fill for a ghost variant — the frost, rim and sweep alone still read unmistakably as glass.
  • Change the sweep angle in the skewX value and the gradient's 105deg; steeper angles look like a faster light source.
  • Swap rounded-full for rounded-2xl if your product language is squarer; nothing else changes.

Gotchas — read before shipping

  • Keep the sweep on translateX, never on left. Animating left triggers layout every frame and is the classic reason a glass button feels cheap on mobile.
  • The fill must stay translucent. bg-lgt02-accent at full opacity gives backdrop-filter nothing to sample and the frost disappears.
  • overflow-hidden is what clips the sweep to the capsule — without it the light beam escapes the button.
  • Check label contrast against the brightest frame of the sweep, not the resting state. A 55%-white beam over a mid accent can dip below 4.5:1 for a few frames.
  • The browser CDN build compiles in the page and is for prototyping only. Ship the CLI or a plugin build.
  • Opacity modifiers work on theme colours (bg-lgt02-accent/70) but not on arbitrary paint values inside a [background:...] arbitrary property.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Utilities only, no JavaScript. Tailwind v4's sheet sets the floor; the underlying mechanisms (backdrop-filter, transforms, gradients) go back much further, and where backdrop-filter is missing the button degrades to a solid accent capsule with the same gloss sweep.

Techniques used in this demo

Theme tokensset in @theme, used by a utility

HTML & JSno Tailwind involved

Search CodeFronts

Loading…