33 / 36
All 36 in 36 Tailwind CSS Button Hover Effects
- 01Tailwind Magnetic Ripple Attraction Button Hover Effect
- 02Tailwind Cyberpunk Holographic Glitch Grid Button
- 03Tailwind Frosted Glassmorphism Shimmer Glow Button
- 04Tailwind Organic Blob Morph Button Hover Effect
- 05Tailwind Dual-Line Intersection Border Draw Button
- 06Tailwind Isometric Tactile 3D Depress Button
- 07Tailwind Infinite Sliding Kinetic Marquee Button
- 08Tailwind Chroma Conic Gradient Border Spin Button
- 09Tailwind Minimalist Border & Outline Drawing Hover Effects
- 10Tailwind Modern 3D Skeuomorphic Button Press Effects
- 11Tailwind Neo-Brutalist & Cyberpunk Button Glow Effects
- 12Tailwind Sliding Background Color Wipe Button Effects
- 13Tailwind Liquid Fill & Organic Morphing Button Effects
- 14Tailwind Kinetic Text Flip & Icon Reveal Button Effects
- 15Tailwind Liquid Wave Fill Button Hover Effect
- 16Tailwind Magnetic Border Drift Button
- 17Tailwind Glitch Slice Button Hover Effect
- 18Tailwind Neon Pulse Glow Button Hover Effect
- 19Tailwind Split Reveal Curtain Button Effect
- 20Tailwind Diagonal Shutter Sweep Button Effect
- 21Tailwind Elastic Lift Spring Button Hover Effect
- 22Tailwind Center Underline Draw Link Button Effects
- 23Tailwind 3D Flip Card Button Hover Effect
- 24Tailwind Particle Burst Confetti Button Effect
- 25Tailwind Slide Doors Closing Panel Button Effect
- 26Tailwind Typewriter Retype Terminal Button Effect
- 27Tailwind Hard Shadow Shift Flat Button Effect
- 28Tailwind Material Ripple Wave Button Effect
- 29Tailwind Rainbow Gradient Border Spin Button
- 30Tailwind Morph Shape Silhouette Button Effects
- 31Tailwind Icon Slide In Directional Button Effect
- 32Tailwind Cursor Spotlight Glow Button
- 33Tailwind Stagger Fill Equaliser Button Effect
- 34Tailwind Sunburst Expand Radiant Button Effect
- 35Tailwind 3D Press Plunger Key Button Effect
- 36Tailwind Add-to-Cart Liquid Glass Ripple Button
Tailwind Stagger Fill Equaliser Button Effect
A rhythmic CTA where several bars rise one after another on hover — an equaliser sweeping across the button. The stagger used to need four :nth-child rules; here each bar carries its own delay-*, which is both shorter and impossible to misread.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<div class="tbh-33-group w-full min-h-svh flex flex-wrap">
<section class="tbh-33a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh33-sans bg-tbh33a-stage" aria-label="Stagger fill button">
<button type="button" class="group/btn relative isolate overflow-hidden cursor-pointer rounded-[10px] border-2 border-tbh33a-bar px-11 py-[17px] text-base font-extrabold text-tbh33a-bar bg-transparent transition-colors duration-[250ms] delay-[160ms] hover:text-tbh33a-onbar focus-visible:text-tbh33a-onbar focus-visible:outline-3 focus-visible:outline-tbh33a-bar focus-visible:outline-offset-[3px]">
<span aria-hidden="true" class="absolute inset-0 z-0 flex">
<i class="flex-1 bg-tbh33a-bar scale-y-0 origin-bottom transition-[scale] duration-300 ease-out delay-0 group-hover/btn:scale-y-100 group-focus-visible/btn:scale-y-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33a-bar scale-y-0 origin-bottom transition-[scale] duration-300 ease-out delay-[50ms] group-hover/btn:scale-y-100 group-focus-visible/btn:scale-y-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33a-bar scale-y-0 origin-bottom transition-[scale] duration-300 ease-out delay-100 group-hover/btn:scale-y-100 group-focus-visible/btn:scale-y-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33a-bar scale-y-0 origin-bottom transition-[scale] duration-300 ease-out delay-150 group-hover/btn:scale-y-100 group-focus-visible/btn:scale-y-100 motion-reduce:transition-none"></i>
</span>
<span class="relative z-[1]">Play set</span>
</button>
</section>
<section class="tbh-33b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh33-sans bg-tbh33b-stage" aria-label="Horizontal blinds fill button">
<button type="button" class="group/btn relative isolate overflow-hidden cursor-pointer rounded-[10px] border-2 border-tbh33b-bar px-[46px] py-[18px] text-base font-extrabold text-tbh33b-bar bg-transparent transition-colors duration-[250ms] delay-[180ms] hover:text-tbh33b-onbar focus-visible:text-tbh33b-onbar focus-visible:outline-3 focus-visible:outline-tbh33b-bar focus-visible:outline-offset-[3px]">
<span aria-hidden="true" class="absolute inset-0 z-0 flex flex-col">
<i class="flex-1 bg-tbh33b-bar scale-x-0 origin-left transition-[scale] duration-[320ms] ease-out delay-0 group-hover/btn:scale-x-100 group-focus-visible/btn:scale-x-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33b-bar scale-x-0 origin-right transition-[scale] duration-[320ms] ease-out delay-[60ms] group-hover/btn:scale-x-100 group-focus-visible/btn:scale-x-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33b-bar scale-x-0 origin-left transition-[scale] duration-[320ms] ease-out delay-[120ms] group-hover/btn:scale-x-100 group-focus-visible/btn:scale-x-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33b-bar scale-x-0 origin-right transition-[scale] duration-[320ms] ease-out delay-[180ms] group-hover/btn:scale-x-100 group-focus-visible/btn:scale-x-100 motion-reduce:transition-none"></i>
</span>
<span class="relative z-[1]">Reveal</span>
</button>
</section>
</div><div class="tbh-33-group w-full min-h-svh flex flex-wrap">
<section class="tbh-33a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh33-sans bg-tbh33a-stage" aria-label="Stagger fill button">
<button type="button" class="group/btn relative isolate overflow-hidden cursor-pointer rounded-[10px] border-2 border-tbh33a-bar px-11 py-[17px] text-base font-extrabold text-tbh33a-bar bg-transparent transition-colors duration-[250ms] delay-[160ms] hover:text-tbh33a-onbar focus-visible:text-tbh33a-onbar focus-visible:outline-3 focus-visible:outline-tbh33a-bar focus-visible:outline-offset-[3px]">
<span aria-hidden="true" class="absolute inset-0 z-0 flex">
<i class="flex-1 bg-tbh33a-bar scale-y-0 origin-bottom transition-[scale] duration-300 ease-out delay-0 group-hover/btn:scale-y-100 group-focus-visible/btn:scale-y-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33a-bar scale-y-0 origin-bottom transition-[scale] duration-300 ease-out delay-[50ms] group-hover/btn:scale-y-100 group-focus-visible/btn:scale-y-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33a-bar scale-y-0 origin-bottom transition-[scale] duration-300 ease-out delay-100 group-hover/btn:scale-y-100 group-focus-visible/btn:scale-y-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33a-bar scale-y-0 origin-bottom transition-[scale] duration-300 ease-out delay-150 group-hover/btn:scale-y-100 group-focus-visible/btn:scale-y-100 motion-reduce:transition-none"></i>
</span>
<span class="relative z-[1]">Play set</span>
</button>
</section>
<section class="tbh-33b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh33-sans bg-tbh33b-stage" aria-label="Horizontal blinds fill button">
<button type="button" class="group/btn relative isolate overflow-hidden cursor-pointer rounded-[10px] border-2 border-tbh33b-bar px-[46px] py-[18px] text-base font-extrabold text-tbh33b-bar bg-transparent transition-colors duration-[250ms] delay-[180ms] hover:text-tbh33b-onbar focus-visible:text-tbh33b-onbar focus-visible:outline-3 focus-visible:outline-tbh33b-bar focus-visible:outline-offset-[3px]">
<span aria-hidden="true" class="absolute inset-0 z-0 flex flex-col">
<i class="flex-1 bg-tbh33b-bar scale-x-0 origin-left transition-[scale] duration-[320ms] ease-out delay-0 group-hover/btn:scale-x-100 group-focus-visible/btn:scale-x-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33b-bar scale-x-0 origin-right transition-[scale] duration-[320ms] ease-out delay-[60ms] group-hover/btn:scale-x-100 group-focus-visible/btn:scale-x-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33b-bar scale-x-0 origin-left transition-[scale] duration-[320ms] ease-out delay-[120ms] group-hover/btn:scale-x-100 group-focus-visible/btn:scale-x-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33b-bar scale-x-0 origin-right transition-[scale] duration-[320ms] ease-out delay-[180ms] group-hover/btn:scale-x-100 group-focus-visible/btn:scale-x-100 motion-reduce:transition-none"></i>
</span>
<span class="relative z-[1]">Reveal</span>
</button>
</section>
</div>@import "tailwindcss";
@theme {
--color-tbh33a-bar: #63d98f;
--color-tbh33a-bar: oklch(0.8 0.19 145);
--color-tbh33a-onbar: #04170d;
--color-tbh33a-onbar: oklch(0.2 0.05 145);
--color-tbh33a-stage: #0a120c;
--color-tbh33a-stage: oklch(0.14 0.015 145);
--color-tbh33b-bar: #7a6ce0;
--color-tbh33b-bar: oklch(0.62 0.17 280);
--color-tbh33b-onbar: #ffffff;
--color-tbh33b-onbar: oklch(1 0 0);
--color-tbh33b-stage: #0d0a1a;
--color-tbh33b-stage: oklch(0.14 0.025 280);
--font-tbh33-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: the four :nth-child delay rules became four delay-* utilities. */
.tbh-33-group {
width: 100%;
min-height: 100svh;
}
.tbh-33a,
.tbh-33b {
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbh33a-bar: #63d98f;
--color-tbh33a-bar: oklch(0.8 0.19 145);
--color-tbh33a-onbar: #04170d;
--color-tbh33a-onbar: oklch(0.2 0.05 145);
--color-tbh33a-stage: #0a120c;
--color-tbh33a-stage: oklch(0.14 0.015 145);
--color-tbh33b-bar: #7a6ce0;
--color-tbh33b-bar: oklch(0.62 0.17 280);
--color-tbh33b-onbar: #ffffff;
--color-tbh33b-onbar: oklch(1 0 0);
--color-tbh33b-stage: #0d0a1a;
--color-tbh33b-stage: oklch(0.14 0.025 280);
--font-tbh33-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: the four :nth-child delay rules became four delay-* utilities. */
.tbh-33-group {
width: 100%;
min-height: 100svh;
}
.tbh-33a,
.tbh-33b {
min-height: 100svh;
}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 {
--color-tbh33a-bar: #63d98f;
--color-tbh33a-bar: oklch(0.8 0.19 145);
--color-tbh33a-onbar: #04170d;
--color-tbh33a-onbar: oklch(0.2 0.05 145);
--color-tbh33a-stage: #0a120c;
--color-tbh33a-stage: oklch(0.14 0.015 145);
--color-tbh33b-bar: #7a6ce0;
--color-tbh33b-bar: oklch(0.62 0.17 280);
--color-tbh33b-onbar: #ffffff;
--color-tbh33b-onbar: oklch(1 0 0);
--color-tbh33b-stage: #0d0a1a;
--color-tbh33b-stage: oklch(0.14 0.025 280);
--font-tbh33-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: the four :nth-child delay rules became four delay-* utilities. */
.tbh-33-group {
width: 100%;
min-height: 100svh;
}
.tbh-33a,
.tbh-33b {
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbh33a-bar: #63d98f;
--color-tbh33a-bar: oklch(0.8 0.19 145);
--color-tbh33a-onbar: #04170d;
--color-tbh33a-onbar: oklch(0.2 0.05 145);
--color-tbh33a-stage: #0a120c;
--color-tbh33a-stage: oklch(0.14 0.015 145);
--color-tbh33b-bar: #7a6ce0;
--color-tbh33b-bar: oklch(0.62 0.17 280);
--color-tbh33b-onbar: #ffffff;
--color-tbh33b-onbar: oklch(1 0 0);
--color-tbh33b-stage: #0d0a1a;
--color-tbh33b-stage: oklch(0.14 0.025 280);
--font-tbh33-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: the four :nth-child delay rules became four delay-* utilities. */
.tbh-33-group {
width: 100%;
min-height: 100svh;
}
.tbh-33a,
.tbh-33b {
min-height: 100svh;
}<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=Space+Grotesk:wght@400;500;600;700&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=Space+Grotesk:wght@400;500;600;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The best
:nth-childremoval in the collection. Four positional selectors become fourdelay-*utilities that ride the bars they describe, so reordering or adding a bar cannot silently break the rhythm. - Design B's alternating
origin-left/origin-rightis a two-word change per bar that would have needed two more:nth-childrules in CSS — the kind of variation the port makes cheap enough to actually try. - UI change from the CSS original: both fills now run on
group-focus-visible/btn:, and the bar colours were lightened (0.7 → 0.8 lightness on A) so the dark on-bar ink clears 4.5:1 once the bars are up.
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 Stagger Fill Equaliser Button Effect
Source: https://codefronts.com/motion/tailwind-css-button-hover-effects/tailwind-stagger-fill-equaliser-button-effect/
A rhythmic CTA where several bars rise one after another on hover — an equaliser sweeping across the button. The stagger used to need four :nth-child rules; here each bar carries its own delay-*, which is both shorter and impossible to misread.
## HTML
```html
<div class="tbh-33-group w-full min-h-svh flex flex-wrap">
<section class="tbh-33a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh33-sans bg-tbh33a-stage" aria-label="Stagger fill button">
<button type="button" class="group/btn relative isolate overflow-hidden cursor-pointer rounded-[10px] border-2 border-tbh33a-bar px-11 py-[17px] text-base font-extrabold text-tbh33a-bar bg-transparent transition-colors duration-[250ms] delay-[160ms] hover:text-tbh33a-onbar focus-visible:text-tbh33a-onbar focus-visible:outline-3 focus-visible:outline-tbh33a-bar focus-visible:outline-offset-[3px]">
<span aria-hidden="true" class="absolute inset-0 z-0 flex">
<i class="flex-1 bg-tbh33a-bar scale-y-0 origin-bottom transition-[scale] duration-300 ease-out delay-0 group-hover/btn:scale-y-100 group-focus-visible/btn:scale-y-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33a-bar scale-y-0 origin-bottom transition-[scale] duration-300 ease-out delay-[50ms] group-hover/btn:scale-y-100 group-focus-visible/btn:scale-y-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33a-bar scale-y-0 origin-bottom transition-[scale] duration-300 ease-out delay-100 group-hover/btn:scale-y-100 group-focus-visible/btn:scale-y-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33a-bar scale-y-0 origin-bottom transition-[scale] duration-300 ease-out delay-150 group-hover/btn:scale-y-100 group-focus-visible/btn:scale-y-100 motion-reduce:transition-none"></i>
</span>
<span class="relative z-[1]">Play set</span>
</button>
</section>
<section class="tbh-33b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh33-sans bg-tbh33b-stage" aria-label="Horizontal blinds fill button">
<button type="button" class="group/btn relative isolate overflow-hidden cursor-pointer rounded-[10px] border-2 border-tbh33b-bar px-[46px] py-[18px] text-base font-extrabold text-tbh33b-bar bg-transparent transition-colors duration-[250ms] delay-[180ms] hover:text-tbh33b-onbar focus-visible:text-tbh33b-onbar focus-visible:outline-3 focus-visible:outline-tbh33b-bar focus-visible:outline-offset-[3px]">
<span aria-hidden="true" class="absolute inset-0 z-0 flex flex-col">
<i class="flex-1 bg-tbh33b-bar scale-x-0 origin-left transition-[scale] duration-[320ms] ease-out delay-0 group-hover/btn:scale-x-100 group-focus-visible/btn:scale-x-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33b-bar scale-x-0 origin-right transition-[scale] duration-[320ms] ease-out delay-[60ms] group-hover/btn:scale-x-100 group-focus-visible/btn:scale-x-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33b-bar scale-x-0 origin-left transition-[scale] duration-[320ms] ease-out delay-[120ms] group-hover/btn:scale-x-100 group-focus-visible/btn:scale-x-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33b-bar scale-x-0 origin-right transition-[scale] duration-[320ms] ease-out delay-[180ms] group-hover/btn:scale-x-100 group-focus-visible/btn:scale-x-100 motion-reduce:transition-none"></i>
</span>
<span class="relative z-[1]">Reveal</span>
</button>
</section>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbh33a-bar: #63d98f;
--color-tbh33a-bar: oklch(0.8 0.19 145);
--color-tbh33a-onbar: #04170d;
--color-tbh33a-onbar: oklch(0.2 0.05 145);
--color-tbh33a-stage: #0a120c;
--color-tbh33a-stage: oklch(0.14 0.015 145);
--color-tbh33b-bar: #7a6ce0;
--color-tbh33b-bar: oklch(0.62 0.17 280);
--color-tbh33b-onbar: #ffffff;
--color-tbh33b-onbar: oklch(1 0 0);
--color-tbh33b-stage: #0d0a1a;
--color-tbh33b-stage: oklch(0.14 0.025 280);
--font-tbh33-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: the four :nth-child delay rules became four delay-* utilities. */
.tbh-33-group {
width: 100%;
min-height: 100svh;
}
.tbh-33a,
.tbh-33b {
min-height: 100svh;
}
```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 Stagger Fill Equaliser Button Effect
Source: https://codefronts.com/motion/tailwind-css-button-hover-effects/tailwind-stagger-fill-equaliser-button-effect/
A rhythmic CTA where several bars rise one after another on hover — an equaliser sweeping across the button. The stagger used to need four :nth-child rules; here each bar carries its own delay-*, which is both shorter and impossible to misread.
## HTML
```html
<div class="tbh-33-group w-full min-h-svh flex flex-wrap">
<section class="tbh-33a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh33-sans bg-tbh33a-stage" aria-label="Stagger fill button">
<button type="button" class="group/btn relative isolate overflow-hidden cursor-pointer rounded-[10px] border-2 border-tbh33a-bar px-11 py-[17px] text-base font-extrabold text-tbh33a-bar bg-transparent transition-colors duration-[250ms] delay-[160ms] hover:text-tbh33a-onbar focus-visible:text-tbh33a-onbar focus-visible:outline-3 focus-visible:outline-tbh33a-bar focus-visible:outline-offset-[3px]">
<span aria-hidden="true" class="absolute inset-0 z-0 flex">
<i class="flex-1 bg-tbh33a-bar scale-y-0 origin-bottom transition-[scale] duration-300 ease-out delay-0 group-hover/btn:scale-y-100 group-focus-visible/btn:scale-y-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33a-bar scale-y-0 origin-bottom transition-[scale] duration-300 ease-out delay-[50ms] group-hover/btn:scale-y-100 group-focus-visible/btn:scale-y-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33a-bar scale-y-0 origin-bottom transition-[scale] duration-300 ease-out delay-100 group-hover/btn:scale-y-100 group-focus-visible/btn:scale-y-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33a-bar scale-y-0 origin-bottom transition-[scale] duration-300 ease-out delay-150 group-hover/btn:scale-y-100 group-focus-visible/btn:scale-y-100 motion-reduce:transition-none"></i>
</span>
<span class="relative z-[1]">Play set</span>
</button>
</section>
<section class="tbh-33b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh33-sans bg-tbh33b-stage" aria-label="Horizontal blinds fill button">
<button type="button" class="group/btn relative isolate overflow-hidden cursor-pointer rounded-[10px] border-2 border-tbh33b-bar px-[46px] py-[18px] text-base font-extrabold text-tbh33b-bar bg-transparent transition-colors duration-[250ms] delay-[180ms] hover:text-tbh33b-onbar focus-visible:text-tbh33b-onbar focus-visible:outline-3 focus-visible:outline-tbh33b-bar focus-visible:outline-offset-[3px]">
<span aria-hidden="true" class="absolute inset-0 z-0 flex flex-col">
<i class="flex-1 bg-tbh33b-bar scale-x-0 origin-left transition-[scale] duration-[320ms] ease-out delay-0 group-hover/btn:scale-x-100 group-focus-visible/btn:scale-x-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33b-bar scale-x-0 origin-right transition-[scale] duration-[320ms] ease-out delay-[60ms] group-hover/btn:scale-x-100 group-focus-visible/btn:scale-x-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33b-bar scale-x-0 origin-left transition-[scale] duration-[320ms] ease-out delay-[120ms] group-hover/btn:scale-x-100 group-focus-visible/btn:scale-x-100 motion-reduce:transition-none"></i>
<i class="flex-1 bg-tbh33b-bar scale-x-0 origin-right transition-[scale] duration-[320ms] ease-out delay-[180ms] group-hover/btn:scale-x-100 group-focus-visible/btn:scale-x-100 motion-reduce:transition-none"></i>
</span>
<span class="relative z-[1]">Reveal</span>
</button>
</section>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbh33a-bar: #63d98f;
--color-tbh33a-bar: oklch(0.8 0.19 145);
--color-tbh33a-onbar: #04170d;
--color-tbh33a-onbar: oklch(0.2 0.05 145);
--color-tbh33a-stage: #0a120c;
--color-tbh33a-stage: oklch(0.14 0.015 145);
--color-tbh33b-bar: #7a6ce0;
--color-tbh33b-bar: oklch(0.62 0.17 280);
--color-tbh33b-onbar: #ffffff;
--color-tbh33b-onbar: oklch(1 0 0);
--color-tbh33b-stage: #0d0a1a;
--color-tbh33b-stage: oklch(0.14 0.025 280);
--font-tbh33-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: the four :nth-child delay rules became four delay-* utilities. */
.tbh-33-group {
width: 100%;
min-height: 100svh;
}
.tbh-33a,
.tbh-33b {
min-height: 100svh;
}
```How this works
The stagger becomes markup. .bars i:nth-child(3){transition-delay:.1s} was a rule that silently breaks if a bar is added or reordered. As delay-100 on the third bar it travels with the element, and the rhythm is legible as a sequence: 0, 50, 100, 150.
Bars must tile exactly. Four flex-1 children of a full-bleed flex track, no gap. A 1px gap at any zoom shows the resting background as vertical seams once the bars are up — which is why this is a flex track rather than four positioned elements.
Design B alternates its origins. Rows 1 and 3 wipe from the left, 2 and 4 from the right. That is a two-word change per bar and it turns a uniform reveal into woven blinds — the kind of variation that is trivial to try when the origin is a utility.
Make it yours
- Bar count: add children and re-space the delays evenly — keep the total under ~250ms or the last bar feels detached.
- Direction:
origin-bottom(rise),origin-top(drop),origin-center(grow). - Reverse the sweep: invert the delay order (150, 100, 50, 0).
- Palette:
--color-tbh33a-barand-onbar; the border, bars, ink and ring all derive from them.
Gotchas — read before shipping
- Bars must tile the full width or height with no gap, or the rest state shows through as seams.
- Keep the per-bar delay small (50–60ms). At 150ms each the effect becomes a loading bar.
- Delay the label's colour swap past the final bar's start (
delay-[160ms]), or the ink flips while a third of the button is still unfilled. - The bar track is
aria-hiddendecoration; the label stays a real text node atz-[1].
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Transforms and transition-delay are universal; with CSS unavailable each button is a plain bordered CTA.