25 / 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 Slide Doors Closing Panel Button Effect
A CTA where two coloured panels slide in from the left and right to meet in the exact centre on hover — like closing doors — restyling the surface as they seal. The inward counterpart to entry 19's outward split, and the pair is worth reading together.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<div class="tbh-25-group w-full min-h-svh flex flex-wrap">
<section class="tbh-25a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh25-sans bg-tbh25a-stage" aria-label="Slide doors button">
<button type="button" class="relative isolate overflow-hidden cursor-pointer rounded-[10px] border-2 border-tbh25a-door px-[42px] py-[17px] text-base font-extrabold text-tbh25a-door bg-white transition-colors duration-[250ms] delay-[180ms] before:content-[''] before:absolute before:top-0 before:left-0 before:w-1/2 before:h-full before:z-0 before:bg-tbh25a-door before:-translate-x-[101%] before:transition-[translate] before:duration-[350ms] before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[''] after:absolute after:top-0 after:right-0 after:w-1/2 after:h-full after:z-0 after:bg-tbh25a-door after:translate-x-[101%] after:transition-[translate] after:duration-[350ms] after:ease-[cubic-bezier(.4,0,.2,1)] hover:text-tbh25a-ondoor hover:before:translate-x-0 hover:after:translate-x-0 focus-visible:text-tbh25a-ondoor focus-visible:before:translate-x-0 focus-visible:after:translate-x-0 focus-visible:outline-3 focus-visible:outline-tbh25a-door focus-visible:outline-offset-[3px] motion-reduce:delay-0 motion-reduce:before:transition-none motion-reduce:after:transition-none">
<span class="relative z-[1]">Open account</span>
</button>
</section>
<section class="tbh-25b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh25-sans bg-tbh25b-stage" aria-label="Vertical shutter doors button">
<button type="button" class="relative isolate overflow-hidden cursor-pointer rounded-[10px] border-2 border-tbh25b-door px-11 py-[18px] text-base font-extrabold text-tbh25b-door bg-white transition-colors duration-[250ms] delay-[180ms] before:content-[''] before:absolute before:left-0 before:top-0 before:w-full before:h-1/2 before:z-0 before:bg-tbh25b-door before:-translate-y-[101%] before:transition-[translate] before:duration-[350ms] before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[''] after:absolute after:left-0 after:bottom-0 after:w-full after:h-1/2 after:z-0 after:bg-tbh25b-door after:translate-y-[101%] after:transition-[translate] after:duration-[350ms] after:ease-[cubic-bezier(.4,0,.2,1)] hover:text-tbh25b-ondoor hover:before:translate-y-0 hover:after:translate-y-0 focus-visible:text-tbh25b-ondoor focus-visible:before:translate-y-0 focus-visible:after:translate-y-0 focus-visible:outline-3 focus-visible:outline-tbh25b-door focus-visible:outline-offset-[3px] motion-reduce:delay-0 motion-reduce:before:transition-none motion-reduce:after:transition-none">
<span class="relative z-[1]">Reveal offer</span>
</button>
</section>
</div><div class="tbh-25-group w-full min-h-svh flex flex-wrap">
<section class="tbh-25a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh25-sans bg-tbh25a-stage" aria-label="Slide doors button">
<button type="button" class="relative isolate overflow-hidden cursor-pointer rounded-[10px] border-2 border-tbh25a-door px-[42px] py-[17px] text-base font-extrabold text-tbh25a-door bg-white transition-colors duration-[250ms] delay-[180ms] before:content-[''] before:absolute before:top-0 before:left-0 before:w-1/2 before:h-full before:z-0 before:bg-tbh25a-door before:-translate-x-[101%] before:transition-[translate] before:duration-[350ms] before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[''] after:absolute after:top-0 after:right-0 after:w-1/2 after:h-full after:z-0 after:bg-tbh25a-door after:translate-x-[101%] after:transition-[translate] after:duration-[350ms] after:ease-[cubic-bezier(.4,0,.2,1)] hover:text-tbh25a-ondoor hover:before:translate-x-0 hover:after:translate-x-0 focus-visible:text-tbh25a-ondoor focus-visible:before:translate-x-0 focus-visible:after:translate-x-0 focus-visible:outline-3 focus-visible:outline-tbh25a-door focus-visible:outline-offset-[3px] motion-reduce:delay-0 motion-reduce:before:transition-none motion-reduce:after:transition-none">
<span class="relative z-[1]">Open account</span>
</button>
</section>
<section class="tbh-25b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh25-sans bg-tbh25b-stage" aria-label="Vertical shutter doors button">
<button type="button" class="relative isolate overflow-hidden cursor-pointer rounded-[10px] border-2 border-tbh25b-door px-11 py-[18px] text-base font-extrabold text-tbh25b-door bg-white transition-colors duration-[250ms] delay-[180ms] before:content-[''] before:absolute before:left-0 before:top-0 before:w-full before:h-1/2 before:z-0 before:bg-tbh25b-door before:-translate-y-[101%] before:transition-[translate] before:duration-[350ms] before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[''] after:absolute after:left-0 after:bottom-0 after:w-full after:h-1/2 after:z-0 after:bg-tbh25b-door after:translate-y-[101%] after:transition-[translate] after:duration-[350ms] after:ease-[cubic-bezier(.4,0,.2,1)] hover:text-tbh25b-ondoor hover:before:translate-y-0 hover:after:translate-y-0 focus-visible:text-tbh25b-ondoor focus-visible:before:translate-y-0 focus-visible:after:translate-y-0 focus-visible:outline-3 focus-visible:outline-tbh25b-door focus-visible:outline-offset-[3px] motion-reduce:delay-0 motion-reduce:before:transition-none motion-reduce:after:transition-none">
<span class="relative z-[1]">Reveal offer</span>
</button>
</section>
</div>@import "tailwindcss";
@theme {
--color-tbh25a-door: #7a4fc0;
--color-tbh25a-door: oklch(0.55 0.15 300);
--color-tbh25a-ondoor: #ffffff;
--color-tbh25a-ondoor: oklch(1 0 0);
--color-tbh25a-stage: #f2eef8;
--color-tbh25a-stage: oklch(0.95 0.015 300);
--color-tbh25b-door: #3d94a4;
--color-tbh25b-door: oklch(0.58 0.16 200);
--color-tbh25b-ondoor: #03181c;
--color-tbh25b-ondoor: oklch(0.2 0.03 200);
--color-tbh25b-stage: #e6f2f4;
--color-tbh25b-stage: oklch(0.95 0.015 200);
--font-tbh25-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: two half-panels and one translate each. */
.tbh-25-group {
width: 100%;
min-height: 100svh;
}
.tbh-25a,
.tbh-25b {
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbh25a-door: #7a4fc0;
--color-tbh25a-door: oklch(0.55 0.15 300);
--color-tbh25a-ondoor: #ffffff;
--color-tbh25a-ondoor: oklch(1 0 0);
--color-tbh25a-stage: #f2eef8;
--color-tbh25a-stage: oklch(0.95 0.015 300);
--color-tbh25b-door: #3d94a4;
--color-tbh25b-door: oklch(0.58 0.16 200);
--color-tbh25b-ondoor: #03181c;
--color-tbh25b-ondoor: oklch(0.2 0.03 200);
--color-tbh25b-stage: #e6f2f4;
--color-tbh25b-stage: oklch(0.95 0.015 200);
--font-tbh25-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: two half-panels and one translate each. */
.tbh-25-group {
width: 100%;
min-height: 100svh;
}
.tbh-25a,
.tbh-25b {
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-tbh25a-door: #7a4fc0;
--color-tbh25a-door: oklch(0.55 0.15 300);
--color-tbh25a-ondoor: #ffffff;
--color-tbh25a-ondoor: oklch(1 0 0);
--color-tbh25a-stage: #f2eef8;
--color-tbh25a-stage: oklch(0.95 0.015 300);
--color-tbh25b-door: #3d94a4;
--color-tbh25b-door: oklch(0.58 0.16 200);
--color-tbh25b-ondoor: #03181c;
--color-tbh25b-ondoor: oklch(0.2 0.03 200);
--color-tbh25b-stage: #e6f2f4;
--color-tbh25b-stage: oklch(0.95 0.015 200);
--font-tbh25-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: two half-panels and one translate each. */
.tbh-25-group {
width: 100%;
min-height: 100svh;
}
.tbh-25a,
.tbh-25b {
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbh25a-door: #7a4fc0;
--color-tbh25a-door: oklch(0.55 0.15 300);
--color-tbh25a-ondoor: #ffffff;
--color-tbh25a-ondoor: oklch(1 0 0);
--color-tbh25a-stage: #f2eef8;
--color-tbh25a-stage: oklch(0.95 0.015 300);
--color-tbh25b-door: #3d94a4;
--color-tbh25b-door: oklch(0.58 0.16 200);
--color-tbh25b-ondoor: #03181c;
--color-tbh25b-ondoor: oklch(0.2 0.03 200);
--color-tbh25b-stage: #e6f2f4;
--color-tbh25b-stage: oklch(0.95 0.015 200);
--font-tbh25-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: two half-panels and one translate each. */
.tbh-25-group {
width: 100%;
min-height: 100svh;
}
.tbh-25a,
.tbh-25b {
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.
- Read alongside entry 19: the two are the same construction in opposite directions, and in Tailwind that relationship is visible at a glance — 19 parts
-translate-y-full/translate-y-fullfrom 0, 25 closes-translate-x-[101%]/translate-x-[101%]to 0. In two CSS files it was a comment you had to trust. delay-[180ms]versus entry 12'sdelay-[120ms]is the one number in this demo that is genuinely different from a plain wipe, and putting it in the class list makes the reason (the centre is covered last) inspectable.- UI change from the CSS original: both door pairs now close on
focus-visible:, and the reduced-motion path drops the label delay to 0 so the colour swap is immediate rather than waiting on doors that no longer travel.
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 Slide Doors Closing Panel Button Effect
Source: https://codefronts.com/motion/tailwind-css-button-hover-effects/tailwind-slide-doors-closing-panel-button-effect/
A CTA where two coloured panels slide in from the left and right to meet in the exact centre on hover — like closing doors — restyling the surface as they seal. The inward counterpart to entry 19's outward split, and the pair is worth reading together.
## HTML
```html
<div class="tbh-25-group w-full min-h-svh flex flex-wrap">
<section class="tbh-25a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh25-sans bg-tbh25a-stage" aria-label="Slide doors button">
<button type="button" class="relative isolate overflow-hidden cursor-pointer rounded-[10px] border-2 border-tbh25a-door px-[42px] py-[17px] text-base font-extrabold text-tbh25a-door bg-white transition-colors duration-[250ms] delay-[180ms] before:content-[''] before:absolute before:top-0 before:left-0 before:w-1/2 before:h-full before:z-0 before:bg-tbh25a-door before:-translate-x-[101%] before:transition-[translate] before:duration-[350ms] before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[''] after:absolute after:top-0 after:right-0 after:w-1/2 after:h-full after:z-0 after:bg-tbh25a-door after:translate-x-[101%] after:transition-[translate] after:duration-[350ms] after:ease-[cubic-bezier(.4,0,.2,1)] hover:text-tbh25a-ondoor hover:before:translate-x-0 hover:after:translate-x-0 focus-visible:text-tbh25a-ondoor focus-visible:before:translate-x-0 focus-visible:after:translate-x-0 focus-visible:outline-3 focus-visible:outline-tbh25a-door focus-visible:outline-offset-[3px] motion-reduce:delay-0 motion-reduce:before:transition-none motion-reduce:after:transition-none">
<span class="relative z-[1]">Open account</span>
</button>
</section>
<section class="tbh-25b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh25-sans bg-tbh25b-stage" aria-label="Vertical shutter doors button">
<button type="button" class="relative isolate overflow-hidden cursor-pointer rounded-[10px] border-2 border-tbh25b-door px-11 py-[18px] text-base font-extrabold text-tbh25b-door bg-white transition-colors duration-[250ms] delay-[180ms] before:content-[''] before:absolute before:left-0 before:top-0 before:w-full before:h-1/2 before:z-0 before:bg-tbh25b-door before:-translate-y-[101%] before:transition-[translate] before:duration-[350ms] before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[''] after:absolute after:left-0 after:bottom-0 after:w-full after:h-1/2 after:z-0 after:bg-tbh25b-door after:translate-y-[101%] after:transition-[translate] after:duration-[350ms] after:ease-[cubic-bezier(.4,0,.2,1)] hover:text-tbh25b-ondoor hover:before:translate-y-0 hover:after:translate-y-0 focus-visible:text-tbh25b-ondoor focus-visible:before:translate-y-0 focus-visible:after:translate-y-0 focus-visible:outline-3 focus-visible:outline-tbh25b-door focus-visible:outline-offset-[3px] motion-reduce:delay-0 motion-reduce:before:transition-none motion-reduce:after:transition-none">
<span class="relative z-[1]">Reveal offer</span>
</button>
</section>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbh25a-door: #7a4fc0;
--color-tbh25a-door: oklch(0.55 0.15 300);
--color-tbh25a-ondoor: #ffffff;
--color-tbh25a-ondoor: oklch(1 0 0);
--color-tbh25a-stage: #f2eef8;
--color-tbh25a-stage: oklch(0.95 0.015 300);
--color-tbh25b-door: #3d94a4;
--color-tbh25b-door: oklch(0.58 0.16 200);
--color-tbh25b-ondoor: #03181c;
--color-tbh25b-ondoor: oklch(0.2 0.03 200);
--color-tbh25b-stage: #e6f2f4;
--color-tbh25b-stage: oklch(0.95 0.015 200);
--font-tbh25-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: two half-panels and one translate each. */
.tbh-25-group {
width: 100%;
min-height: 100svh;
}
.tbh-25a,
.tbh-25b {
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 Slide Doors Closing Panel Button Effect
Source: https://codefronts.com/motion/tailwind-css-button-hover-effects/tailwind-slide-doors-closing-panel-button-effect/
A CTA where two coloured panels slide in from the left and right to meet in the exact centre on hover — like closing doors — restyling the surface as they seal. The inward counterpart to entry 19's outward split, and the pair is worth reading together.
## HTML
```html
<div class="tbh-25-group w-full min-h-svh flex flex-wrap">
<section class="tbh-25a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh25-sans bg-tbh25a-stage" aria-label="Slide doors button">
<button type="button" class="relative isolate overflow-hidden cursor-pointer rounded-[10px] border-2 border-tbh25a-door px-[42px] py-[17px] text-base font-extrabold text-tbh25a-door bg-white transition-colors duration-[250ms] delay-[180ms] before:content-[''] before:absolute before:top-0 before:left-0 before:w-1/2 before:h-full before:z-0 before:bg-tbh25a-door before:-translate-x-[101%] before:transition-[translate] before:duration-[350ms] before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[''] after:absolute after:top-0 after:right-0 after:w-1/2 after:h-full after:z-0 after:bg-tbh25a-door after:translate-x-[101%] after:transition-[translate] after:duration-[350ms] after:ease-[cubic-bezier(.4,0,.2,1)] hover:text-tbh25a-ondoor hover:before:translate-x-0 hover:after:translate-x-0 focus-visible:text-tbh25a-ondoor focus-visible:before:translate-x-0 focus-visible:after:translate-x-0 focus-visible:outline-3 focus-visible:outline-tbh25a-door focus-visible:outline-offset-[3px] motion-reduce:delay-0 motion-reduce:before:transition-none motion-reduce:after:transition-none">
<span class="relative z-[1]">Open account</span>
</button>
</section>
<section class="tbh-25b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh25-sans bg-tbh25b-stage" aria-label="Vertical shutter doors button">
<button type="button" class="relative isolate overflow-hidden cursor-pointer rounded-[10px] border-2 border-tbh25b-door px-11 py-[18px] text-base font-extrabold text-tbh25b-door bg-white transition-colors duration-[250ms] delay-[180ms] before:content-[''] before:absolute before:left-0 before:top-0 before:w-full before:h-1/2 before:z-0 before:bg-tbh25b-door before:-translate-y-[101%] before:transition-[translate] before:duration-[350ms] before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[''] after:absolute after:left-0 after:bottom-0 after:w-full after:h-1/2 after:z-0 after:bg-tbh25b-door after:translate-y-[101%] after:transition-[translate] after:duration-[350ms] after:ease-[cubic-bezier(.4,0,.2,1)] hover:text-tbh25b-ondoor hover:before:translate-y-0 hover:after:translate-y-0 focus-visible:text-tbh25b-ondoor focus-visible:before:translate-y-0 focus-visible:after:translate-y-0 focus-visible:outline-3 focus-visible:outline-tbh25b-door focus-visible:outline-offset-[3px] motion-reduce:delay-0 motion-reduce:before:transition-none motion-reduce:after:transition-none">
<span class="relative z-[1]">Reveal offer</span>
</button>
</section>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbh25a-door: #7a4fc0;
--color-tbh25a-door: oklch(0.55 0.15 300);
--color-tbh25a-ondoor: #ffffff;
--color-tbh25a-ondoor: oklch(1 0 0);
--color-tbh25a-stage: #f2eef8;
--color-tbh25a-stage: oklch(0.95 0.015 300);
--color-tbh25b-door: #3d94a4;
--color-tbh25b-door: oklch(0.58 0.16 200);
--color-tbh25b-ondoor: #03181c;
--color-tbh25b-ondoor: oklch(0.2 0.03 200);
--color-tbh25b-stage: #e6f2f4;
--color-tbh25b-stage: oklch(0.95 0.015 200);
--font-tbh25-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: two half-panels and one translate each. */
.tbh-25-group {
width: 100%;
min-height: 100svh;
}
.tbh-25a,
.tbh-25b {
min-height: 100svh;
}
```How this works
Each door is exactly half. w-1/2 h-full with one pinned left and one pinned right; they meet at the centre with no seam and no overlap, because together they are exactly 100%. Make them 51% 'for safety' and the seam becomes a visible dark line where they stack.
101%, not 100%. Parking a door at exactly its own width can leave a sub-pixel edge showing at some zoom levels. The extra 1% costs nothing and removes the whole class of bug.
The delay is longer here than in a wipe. 180ms rather than 120ms, because the doors travel from both edges and the centre — where the label is — is the LAST thing covered. Flip the ink too early and the text is unreadable against the resting fill.
Make it yours
- Axis: design A closes horizontally, B vertically. Swapping is two translate utilities per door.
- Stagger: give the second door
hover:after:delay-[60ms]for an off-kilter close. - Palette:
--color-tbh25a-doorand-ondoor— the ink that must be legible once the doors have sealed. - Pace:
before:duration-[350ms]; slower reads as heavy doors, faster as a snap shutter.
Gotchas — read before shipping
- Each door must be exactly 50% (or 50% height). Anything more and the doors overlap into a visible seam; anything less and the fill shows through the middle.
- Delay the label's colour swap by roughly half the door duration — the centre is covered last, unlike a one-directional wipe.
- Both ink colours need checking: resting ink on the panel background, and
-ondoorink on the door colour. overflow-hiddenplusisolate: the first keeps the parked doors invisible, the second contains theirz-0beneath the label'sz-[1].
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Transforms and pseudo-elements are universal; with CSS unavailable each button is a plain bordered CTA.