14 / 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 Kinetic Text Flip & Icon Reveal Button Effects
A snappy micro-interaction: on hover the label slides up and out while a second line or icon slides up to replace it, inside a clipped window — the 'Add to cart → Added ✓' pattern. The port's discipline is accessibility: the second row is aria-hidden, so the accessible name never changes under the user.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<div class="tbh-14-group w-full min-h-svh flex flex-wrap">
<section class="tbh-14a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh14-sans bg-tbh14a-stage" aria-label="Text flip reveal button">
<button type="button" class="group/btn overflow-hidden h-14 min-w-[180px] cursor-pointer border-0 rounded-xl p-0 text-base font-extrabold text-white bg-tbh14a-acc focus-visible:outline-3 focus-visible:outline-tbh14a-acc focus-visible:outline-offset-[3px]">
<span class="flex flex-col w-full transition-[translate] duration-400 ease-[cubic-bezier(.2,.9,.2,1)] group-hover/btn:-translate-y-1/2 group-focus-visible/btn:-translate-y-1/2 motion-reduce:transition-none">
<span class="flex items-center justify-center h-14 w-full px-10">Add to cart</span>
<span aria-hidden="true" class="flex items-center justify-center h-14 w-full px-10 bg-tbh14a-deep">Added ✓</span>
</span>
</button>
</section>
<section class="tbh-14b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh14-sans bg-tbh14b-stage" aria-label="Label to icon button">
<button type="button" aria-label="Next step" class="group/btn overflow-hidden h-14 w-[180px] cursor-pointer border-0 rounded-full text-base font-extrabold text-white bg-tbh14b-acc focus-visible:outline-3 focus-visible:outline-white focus-visible:outline-offset-[3px]">
<span class="flex flex-col transition-[translate] duration-[380ms] ease-[cubic-bezier(.2,.9,.2,1)] group-hover/btn:-translate-y-[56px] group-focus-visible/btn:-translate-y-[56px] motion-reduce:transition-none">
<span class="flex items-center justify-center h-14 text-base">Next step</span>
<span aria-hidden="true" class="flex items-center justify-center h-14 text-xl">→</span>
</span>
</button>
</section>
</div><div class="tbh-14-group w-full min-h-svh flex flex-wrap">
<section class="tbh-14a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh14-sans bg-tbh14a-stage" aria-label="Text flip reveal button">
<button type="button" class="group/btn overflow-hidden h-14 min-w-[180px] cursor-pointer border-0 rounded-xl p-0 text-base font-extrabold text-white bg-tbh14a-acc focus-visible:outline-3 focus-visible:outline-tbh14a-acc focus-visible:outline-offset-[3px]">
<span class="flex flex-col w-full transition-[translate] duration-400 ease-[cubic-bezier(.2,.9,.2,1)] group-hover/btn:-translate-y-1/2 group-focus-visible/btn:-translate-y-1/2 motion-reduce:transition-none">
<span class="flex items-center justify-center h-14 w-full px-10">Add to cart</span>
<span aria-hidden="true" class="flex items-center justify-center h-14 w-full px-10 bg-tbh14a-deep">Added ✓</span>
</span>
</button>
</section>
<section class="tbh-14b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh14-sans bg-tbh14b-stage" aria-label="Label to icon button">
<button type="button" aria-label="Next step" class="group/btn overflow-hidden h-14 w-[180px] cursor-pointer border-0 rounded-full text-base font-extrabold text-white bg-tbh14b-acc focus-visible:outline-3 focus-visible:outline-white focus-visible:outline-offset-[3px]">
<span class="flex flex-col transition-[translate] duration-[380ms] ease-[cubic-bezier(.2,.9,.2,1)] group-hover/btn:-translate-y-[56px] group-focus-visible/btn:-translate-y-[56px] motion-reduce:transition-none">
<span class="flex items-center justify-center h-14 text-base">Next step</span>
<span aria-hidden="true" class="flex items-center justify-center h-14 text-xl">→</span>
</span>
</button>
</section>
</div>@import "tailwindcss";
@theme {
--color-tbh14a-acc: #4a9e6e;
--color-tbh14a-acc: oklch(0.58 0.15 155);
--color-tbh14a-deep: #3d8a5e;
--color-tbh14a-deep: oklch(0.5 0.15 155);
--color-tbh14a-stage: #0f1512;
--color-tbh14a-stage: oklch(0.17 0.015 155);
--color-tbh14b-acc: #6155db;
--color-tbh14b-acc: oklch(0.56 0.17 275);
--color-tbh14b-stage: #0d0b1a;
--color-tbh14b-stage: oklch(0.15 0.03 275);
--font-tbh14-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: a clipped window, a flex column and one translate per design. */
.tbh-14-group {
width: 100%;
min-height: 100svh;
}
.tbh-14a,
.tbh-14b {
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbh14a-acc: #4a9e6e;
--color-tbh14a-acc: oklch(0.58 0.15 155);
--color-tbh14a-deep: #3d8a5e;
--color-tbh14a-deep: oklch(0.5 0.15 155);
--color-tbh14a-stage: #0f1512;
--color-tbh14a-stage: oklch(0.17 0.015 155);
--color-tbh14b-acc: #6155db;
--color-tbh14b-acc: oklch(0.56 0.17 275);
--color-tbh14b-stage: #0d0b1a;
--color-tbh14b-stage: oklch(0.15 0.03 275);
--font-tbh14-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: a clipped window, a flex column and one translate per design. */
.tbh-14-group {
width: 100%;
min-height: 100svh;
}
.tbh-14a,
.tbh-14b {
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-tbh14a-acc: #4a9e6e;
--color-tbh14a-acc: oklch(0.58 0.15 155);
--color-tbh14a-deep: #3d8a5e;
--color-tbh14a-deep: oklch(0.5 0.15 155);
--color-tbh14a-stage: #0f1512;
--color-tbh14a-stage: oklch(0.17 0.015 155);
--color-tbh14b-acc: #6155db;
--color-tbh14b-acc: oklch(0.56 0.17 275);
--color-tbh14b-stage: #0d0b1a;
--color-tbh14b-stage: oklch(0.15 0.03 275);
--font-tbh14-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: a clipped window, a flex column and one translate per design. */
.tbh-14-group {
width: 100%;
min-height: 100svh;
}
.tbh-14a,
.tbh-14b {
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbh14a-acc: #4a9e6e;
--color-tbh14a-acc: oklch(0.58 0.15 155);
--color-tbh14a-deep: #3d8a5e;
--color-tbh14a-deep: oklch(0.5 0.15 155);
--color-tbh14a-stage: #0f1512;
--color-tbh14a-stage: oklch(0.17 0.015 155);
--color-tbh14b-acc: #6155db;
--color-tbh14b-acc: oklch(0.56 0.17 275);
--color-tbh14b-stage: #0d0b1a;
--color-tbh14b-stage: oklch(0.15 0.03 275);
--font-tbh14-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: a clipped window, a flex column and one translate per design. */
.tbh-14-group {
width: 100%;
min-height: 100svh;
}
.tbh-14a,
.tbh-14b {
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 simplest port in the batch — four selectors to one class list — and the one where the accessibility contract is the substance. Marking the reveal row
aria-hiddenis what keeps a decorative flip from rewriting the button's accessible name. group-hover/btn:-translate-y-1/2versus design B's-translate-y-[56px]is a real design decision rather than a style preference: percentage for identical rows, pixels for mismatched ones. Both are now visible in the markup, which the CSS version could not show.- UI change from the CSS original: both designs mirror the flip on
group-focus-visible/btn:, and design A's reveal row gained a darker tint so the swapped state is distinguishable from the resting one at a glance.
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 Kinetic Text Flip & Icon Reveal Button Effects
Source: https://codefronts.com/motion/tailwind-css-button-hover-effects/tailwind-kinetic-text-flip-icon-reveal-button-effects/
A snappy micro-interaction: on hover the label slides up and out while a second line or icon slides up to replace it, inside a clipped window — the 'Add to cart → Added ✓' pattern. The port's discipline is accessibility: the second row is aria-hidden, so the accessible name never changes under the user.
## HTML
```html
<div class="tbh-14-group w-full min-h-svh flex flex-wrap">
<section class="tbh-14a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh14-sans bg-tbh14a-stage" aria-label="Text flip reveal button">
<button type="button" class="group/btn overflow-hidden h-14 min-w-[180px] cursor-pointer border-0 rounded-xl p-0 text-base font-extrabold text-white bg-tbh14a-acc focus-visible:outline-3 focus-visible:outline-tbh14a-acc focus-visible:outline-offset-[3px]">
<span class="flex flex-col w-full transition-[translate] duration-400 ease-[cubic-bezier(.2,.9,.2,1)] group-hover/btn:-translate-y-1/2 group-focus-visible/btn:-translate-y-1/2 motion-reduce:transition-none">
<span class="flex items-center justify-center h-14 w-full px-10">Add to cart</span>
<span aria-hidden="true" class="flex items-center justify-center h-14 w-full px-10 bg-tbh14a-deep">Added ✓</span>
</span>
</button>
</section>
<section class="tbh-14b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh14-sans bg-tbh14b-stage" aria-label="Label to icon button">
<button type="button" aria-label="Next step" class="group/btn overflow-hidden h-14 w-[180px] cursor-pointer border-0 rounded-full text-base font-extrabold text-white bg-tbh14b-acc focus-visible:outline-3 focus-visible:outline-white focus-visible:outline-offset-[3px]">
<span class="flex flex-col transition-[translate] duration-[380ms] ease-[cubic-bezier(.2,.9,.2,1)] group-hover/btn:-translate-y-[56px] group-focus-visible/btn:-translate-y-[56px] motion-reduce:transition-none">
<span class="flex items-center justify-center h-14 text-base">Next step</span>
<span aria-hidden="true" class="flex items-center justify-center h-14 text-xl">→</span>
</span>
</button>
</section>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbh14a-acc: #4a9e6e;
--color-tbh14a-acc: oklch(0.58 0.15 155);
--color-tbh14a-deep: #3d8a5e;
--color-tbh14a-deep: oklch(0.5 0.15 155);
--color-tbh14a-stage: #0f1512;
--color-tbh14a-stage: oklch(0.17 0.015 155);
--color-tbh14b-acc: #6155db;
--color-tbh14b-acc: oklch(0.56 0.17 275);
--color-tbh14b-stage: #0d0b1a;
--color-tbh14b-stage: oklch(0.15 0.03 275);
--font-tbh14-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: a clipped window, a flex column and one translate per design. */
.tbh-14-group {
width: 100%;
min-height: 100svh;
}
.tbh-14a,
.tbh-14b {
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 Kinetic Text Flip & Icon Reveal Button Effects
Source: https://codefronts.com/motion/tailwind-css-button-hover-effects/tailwind-kinetic-text-flip-icon-reveal-button-effects/
A snappy micro-interaction: on hover the label slides up and out while a second line or icon slides up to replace it, inside a clipped window — the 'Add to cart → Added ✓' pattern. The port's discipline is accessibility: the second row is aria-hidden, so the accessible name never changes under the user.
## HTML
```html
<div class="tbh-14-group w-full min-h-svh flex flex-wrap">
<section class="tbh-14a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh14-sans bg-tbh14a-stage" aria-label="Text flip reveal button">
<button type="button" class="group/btn overflow-hidden h-14 min-w-[180px] cursor-pointer border-0 rounded-xl p-0 text-base font-extrabold text-white bg-tbh14a-acc focus-visible:outline-3 focus-visible:outline-tbh14a-acc focus-visible:outline-offset-[3px]">
<span class="flex flex-col w-full transition-[translate] duration-400 ease-[cubic-bezier(.2,.9,.2,1)] group-hover/btn:-translate-y-1/2 group-focus-visible/btn:-translate-y-1/2 motion-reduce:transition-none">
<span class="flex items-center justify-center h-14 w-full px-10">Add to cart</span>
<span aria-hidden="true" class="flex items-center justify-center h-14 w-full px-10 bg-tbh14a-deep">Added ✓</span>
</span>
</button>
</section>
<section class="tbh-14b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh14-sans bg-tbh14b-stage" aria-label="Label to icon button">
<button type="button" aria-label="Next step" class="group/btn overflow-hidden h-14 w-[180px] cursor-pointer border-0 rounded-full text-base font-extrabold text-white bg-tbh14b-acc focus-visible:outline-3 focus-visible:outline-white focus-visible:outline-offset-[3px]">
<span class="flex flex-col transition-[translate] duration-[380ms] ease-[cubic-bezier(.2,.9,.2,1)] group-hover/btn:-translate-y-[56px] group-focus-visible/btn:-translate-y-[56px] motion-reduce:transition-none">
<span class="flex items-center justify-center h-14 text-base">Next step</span>
<span aria-hidden="true" class="flex items-center justify-center h-14 text-xl">→</span>
</span>
</button>
</section>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbh14a-acc: #4a9e6e;
--color-tbh14a-acc: oklch(0.58 0.15 155);
--color-tbh14a-deep: #3d8a5e;
--color-tbh14a-deep: oklch(0.5 0.15 155);
--color-tbh14a-stage: #0f1512;
--color-tbh14a-stage: oklch(0.17 0.015 155);
--color-tbh14b-acc: #6155db;
--color-tbh14b-acc: oklch(0.56 0.17 275);
--color-tbh14b-stage: #0d0b1a;
--color-tbh14b-stage: oklch(0.15 0.03 275);
--font-tbh14-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: a clipped window, a flex column and one translate per design. */
.tbh-14-group {
width: 100%;
min-height: 100svh;
}
.tbh-14a,
.tbh-14b {
min-height: 100svh;
}
```How this works
The window is the whole trick. Fix the button's height, clip it, and stack two rows of exactly that height inside. Moving the column up by half its height (or one row height) puts row two exactly where row one was — no fading, no absolute positioning, no measurement.
Percentage or pixels, pick per design. Design A's rows are identical, so -translate-y-1/2 is self-maintaining. Design B's rows differ in font size, so the translate is pinned to -56px, the explicit row height. Getting this wrong is the pattern's only real failure mode, and the gotchas say which to use when.
The second row can be anything. A confirmation phrase, an arrow, a spinner, a price — the mechanism does not care, which is why this entry is worth keeping generic.
Make it yours
- Reveal content: replace the second row's text with an icon, a price, or a check — keep the row height fixed.
- Flip speed:
duration-400; 250ms feels eager, 500ms feels laboured. - Axis: swap to
group-hover/btn:-translate-x-1/2with aflex-rowtrack for a horizontal flip. - Row tinting: design A darkens its reveal row (
bg-tbh14a-deep) so the swap reads even in a screenshot.
Gotchas — read before shipping
- Both rows must be exactly the window height. A 2px discrepancy shows as a sliver of the next row at rest.
- Translate by 50% only when the rows are identical in height; otherwise pin the pixel value, as design B does.
- The reveal row MUST be
aria-hidden. Two visible labels in one button means the accessible name becomes 'Add to cart Added', which is worse than no effect at all. - Design B's glyph row sets a larger
text-xl, so its row keeps an explicith-14— font-size must never be allowed to set the row height here.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Flexbox, overflow-hidden and transforms are universal; with CSS unavailable both rows render stacked and the button still works.