05 / 30
All 30 in 30 Tailwind CSS Hover Effects
- 01Tailwind Underline Draw Hover Effect
- 02Tailwind Text Glitch Hover Effect
- 03Tailwind Letter Spacing Expand Hover Effect
- 04Tailwind Gradient Text Reveal Hover Effect
- 05Tailwind Split Text Hover Effect
- 06Tailwind Neon Glow Text Hover Effect
- 07Tailwind Magnetic Liquid Button Hover Effect
- 08Tailwind Border Draw Hover Effect
- 09Tailwind Shimmer Shine Button Hover Effect
- 10Tailwind Fill Wipe Button Hover Effect
- 11Tailwind 3D Press Button Hover Effect
- 12Tailwind Ripple Button Hover Effect
- 13Tailwind 3D Tilt Card Hover Effect
- 14Tailwind Card Flip Hover Effect
- 15Tailwind Glassmorphism Card Hover Effect
- 16Tailwind Spotlight Card Hover Effect
- 17Tailwind Slide Reveal Card Hover Effect
- 18Tailwind Stack Lift Card Hover Effect
- 19Tailwind Zoom Pan Image Hover Effect
- 20Tailwind Color Channel Split Hover Effect
- 21Tailwind Duotone Image Hover Effect
- 22Tailwind Curtain Reveal Image Hover Effect
- 23Tailwind Ken Burns Image Hover Effect
- 24Tailwind Image Tilt Depth Hover Effect
- 25Tailwind Underline Slide Nav Hover Effect
- 26Tailwind Highlight Fill Nav Hover Effect
- 27Tailwind Strikethrough Hover Link Effect
- 28Tailwind Inline Word Swap Hover Effect
- 29Tailwind Dot Trail Cursor Hover Effect
- 30Tailwind Magnetic Cursor Pull Hover Effect
Tailwind Split Text Hover Effect
Four hovers that cut a word in half and push the pieces apart — top/bottom, left/right, a diagonal polygon and a split-plus-fade. Two clipped copies of the text live in before: and after:; the base element goes transparent so nothing shows through the seam.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="thv-05 w-full min-h-svh grid place-items-center bg-thv05-bg font-thv05-sans px-6 py-14">
<div class="w-[min(820px,100%)] grid grid-cols-[repeat(auto-fit,minmax(260px,1fr))] gap-10">
<div class="flex flex-col items-center gap-6 px-8 py-14 rounded-2xl bg-white/[0.025] border border-white/[0.07]">
<span tabindex="0" data-text="DIVIDE" class="relative inline-block overflow-hidden text-[clamp(2rem,6vw,3rem)] font-black tracking-[0.1em] text-transparent rounded-xs focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-thv05-cyan before:content-[attr(data-text)] before:absolute before:inset-0 before:text-thv05-cyan before:[clip-path:inset(0_0_50%_0)] before:transition-transform before:duration-450 before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[attr(data-text)] after:absolute after:inset-0 after:text-thv05-cyan after:[clip-path:inset(50%_0_0_0)] after:transition-transform after:duration-450 after:ease-[cubic-bezier(.4,0,.2,1)] hover:before:-translate-y-[0.55em] hover:after:translate-y-[0.55em] focus-visible:before:-translate-y-[0.55em] focus-visible:after:translate-y-[0.55em] motion-reduce:before:transition-none motion-reduce:after:transition-none">DIVIDE</span>
<span class="text-[11px] tracking-[0.15em] uppercase text-thv05-dim">top / bottom split</span>
</div>
<div class="flex flex-col items-center gap-6 px-8 py-14 rounded-2xl bg-white/[0.025] border border-white/[0.07]">
<span tabindex="0" data-text="SCHISM" class="relative inline-block overflow-hidden text-[clamp(2rem,6vw,3rem)] font-black tracking-[0.1em] text-transparent rounded-xs focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-thv05-rose before:content-[attr(data-text)] before:absolute before:inset-0 before:text-thv05-rose before:[clip-path:inset(0_50%_0_0)] before:transition-transform before:duration-450 before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[attr(data-text)] after:absolute after:inset-0 after:text-thv05-amber after:[clip-path:inset(0_0_0_50%)] after:transition-transform after:duration-450 after:ease-[cubic-bezier(.4,0,.2,1)] hover:before:-translate-x-[0.4em] hover:after:translate-x-[0.4em] focus-visible:before:-translate-x-[0.4em] focus-visible:after:translate-x-[0.4em] motion-reduce:before:transition-none motion-reduce:after:transition-none">SCHISM</span>
<span class="text-[11px] tracking-[0.15em] uppercase text-thv05-dim">left / right push</span>
</div>
<div class="flex flex-col items-center gap-6 px-8 py-14 rounded-2xl bg-white/[0.025] border border-white/[0.07]">
<span tabindex="0" data-text="SLASH" class="relative inline-block text-[clamp(2rem,6vw,3rem)] font-black tracking-[0.1em] text-transparent rounded-xs focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-thv05-violet before:content-[attr(data-text)] before:absolute before:inset-0 before:text-thv05-violet before:[clip-path:polygon(0_0,100%_0,60%_100%,0_100%)] before:transition-transform before:duration-450 before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[attr(data-text)] after:absolute after:inset-0 after:text-thv05-cyan after:[clip-path:polygon(60%_0,100%_0,100%_100%,0_100%)] after:transition-transform after:duration-450 after:ease-[cubic-bezier(.4,0,.2,1)] hover:before:-translate-x-[0.3em] hover:before:-translate-y-[0.3em] hover:after:translate-x-[0.3em] hover:after:translate-y-[0.3em] focus-visible:before:-translate-x-[0.3em] focus-visible:before:-translate-y-[0.3em] focus-visible:after:translate-x-[0.3em] focus-visible:after:translate-y-[0.3em] motion-reduce:before:transition-none motion-reduce:after:transition-none">SLASH</span>
<span class="text-[11px] tracking-[0.15em] uppercase text-thv05-dim">diagonal polygon</span>
</div>
<div class="flex flex-col items-center gap-6 px-8 py-14 rounded-2xl bg-white/[0.025] border border-white/[0.07]">
<span tabindex="0" data-text="RIFT" class="relative inline-block text-[clamp(2rem,6vw,3rem)] font-black tracking-[0.1em] text-transparent rounded-xs focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-thv05-amber before:content-[attr(data-text)] before:absolute before:inset-0 before:text-thv05-amber before:[clip-path:inset(0_0_50%_0)] before:transition-[transform,opacity] before:duration-450 before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[attr(data-text)] after:absolute after:inset-0 after:text-thv05-rose after:[clip-path:inset(50%_0_0_0)] after:transition-[transform,opacity] after:duration-450 after:ease-[cubic-bezier(.4,0,.2,1)] hover:before:-translate-y-[0.6em] hover:before:-rotate-3 hover:before:opacity-0 hover:after:translate-y-[0.6em] hover:after:rotate-3 hover:after:opacity-0 focus-visible:before:-translate-y-[0.6em] focus-visible:before:opacity-0 focus-visible:after:translate-y-[0.6em] focus-visible:after:opacity-0 motion-reduce:before:transition-none motion-reduce:after:transition-none">RIFT</span>
<span class="text-[11px] tracking-[0.15em] uppercase text-thv05-dim">split + fade</span>
</div>
</div>
</section><section class="thv-05 w-full min-h-svh grid place-items-center bg-thv05-bg font-thv05-sans px-6 py-14">
<div class="w-[min(820px,100%)] grid grid-cols-[repeat(auto-fit,minmax(260px,1fr))] gap-10">
<div class="flex flex-col items-center gap-6 px-8 py-14 rounded-2xl bg-white/[0.025] border border-white/[0.07]">
<span tabindex="0" data-text="DIVIDE" class="relative inline-block overflow-hidden text-[clamp(2rem,6vw,3rem)] font-black tracking-[0.1em] text-transparent rounded-xs focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-thv05-cyan before:content-[attr(data-text)] before:absolute before:inset-0 before:text-thv05-cyan before:[clip-path:inset(0_0_50%_0)] before:transition-transform before:duration-450 before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[attr(data-text)] after:absolute after:inset-0 after:text-thv05-cyan after:[clip-path:inset(50%_0_0_0)] after:transition-transform after:duration-450 after:ease-[cubic-bezier(.4,0,.2,1)] hover:before:-translate-y-[0.55em] hover:after:translate-y-[0.55em] focus-visible:before:-translate-y-[0.55em] focus-visible:after:translate-y-[0.55em] motion-reduce:before:transition-none motion-reduce:after:transition-none">DIVIDE</span>
<span class="text-[11px] tracking-[0.15em] uppercase text-thv05-dim">top / bottom split</span>
</div>
<div class="flex flex-col items-center gap-6 px-8 py-14 rounded-2xl bg-white/[0.025] border border-white/[0.07]">
<span tabindex="0" data-text="SCHISM" class="relative inline-block overflow-hidden text-[clamp(2rem,6vw,3rem)] font-black tracking-[0.1em] text-transparent rounded-xs focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-thv05-rose before:content-[attr(data-text)] before:absolute before:inset-0 before:text-thv05-rose before:[clip-path:inset(0_50%_0_0)] before:transition-transform before:duration-450 before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[attr(data-text)] after:absolute after:inset-0 after:text-thv05-amber after:[clip-path:inset(0_0_0_50%)] after:transition-transform after:duration-450 after:ease-[cubic-bezier(.4,0,.2,1)] hover:before:-translate-x-[0.4em] hover:after:translate-x-[0.4em] focus-visible:before:-translate-x-[0.4em] focus-visible:after:translate-x-[0.4em] motion-reduce:before:transition-none motion-reduce:after:transition-none">SCHISM</span>
<span class="text-[11px] tracking-[0.15em] uppercase text-thv05-dim">left / right push</span>
</div>
<div class="flex flex-col items-center gap-6 px-8 py-14 rounded-2xl bg-white/[0.025] border border-white/[0.07]">
<span tabindex="0" data-text="SLASH" class="relative inline-block text-[clamp(2rem,6vw,3rem)] font-black tracking-[0.1em] text-transparent rounded-xs focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-thv05-violet before:content-[attr(data-text)] before:absolute before:inset-0 before:text-thv05-violet before:[clip-path:polygon(0_0,100%_0,60%_100%,0_100%)] before:transition-transform before:duration-450 before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[attr(data-text)] after:absolute after:inset-0 after:text-thv05-cyan after:[clip-path:polygon(60%_0,100%_0,100%_100%,0_100%)] after:transition-transform after:duration-450 after:ease-[cubic-bezier(.4,0,.2,1)] hover:before:-translate-x-[0.3em] hover:before:-translate-y-[0.3em] hover:after:translate-x-[0.3em] hover:after:translate-y-[0.3em] focus-visible:before:-translate-x-[0.3em] focus-visible:before:-translate-y-[0.3em] focus-visible:after:translate-x-[0.3em] focus-visible:after:translate-y-[0.3em] motion-reduce:before:transition-none motion-reduce:after:transition-none">SLASH</span>
<span class="text-[11px] tracking-[0.15em] uppercase text-thv05-dim">diagonal polygon</span>
</div>
<div class="flex flex-col items-center gap-6 px-8 py-14 rounded-2xl bg-white/[0.025] border border-white/[0.07]">
<span tabindex="0" data-text="RIFT" class="relative inline-block text-[clamp(2rem,6vw,3rem)] font-black tracking-[0.1em] text-transparent rounded-xs focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-thv05-amber before:content-[attr(data-text)] before:absolute before:inset-0 before:text-thv05-amber before:[clip-path:inset(0_0_50%_0)] before:transition-[transform,opacity] before:duration-450 before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[attr(data-text)] after:absolute after:inset-0 after:text-thv05-rose after:[clip-path:inset(50%_0_0_0)] after:transition-[transform,opacity] after:duration-450 after:ease-[cubic-bezier(.4,0,.2,1)] hover:before:-translate-y-[0.6em] hover:before:-rotate-3 hover:before:opacity-0 hover:after:translate-y-[0.6em] hover:after:rotate-3 hover:after:opacity-0 focus-visible:before:-translate-y-[0.6em] focus-visible:before:opacity-0 focus-visible:after:translate-y-[0.6em] focus-visible:after:opacity-0 motion-reduce:before:transition-none motion-reduce:after:transition-none">RIFT</span>
<span class="text-[11px] tracking-[0.15em] uppercase text-thv05-dim">split + fade</span>
</div>
</div>
</section>@import "tailwindcss";
@theme {
--color-thv05-bg: #020917;
--color-thv05-bg: oklch(0.13 0.03 260);
--color-thv05-dim: #475569;
--color-thv05-dim: oklch(0.45 0.03 257);
--color-thv05-cyan: #06b6d4;
--color-thv05-cyan: oklch(0.71 0.14 208);
--color-thv05-rose: #f43f5e;
--color-thv05-rose: oklch(0.65 0.23 16);
--color-thv05-violet: #8b5cf6;
--color-thv05-violet: oklch(0.63 0.21 295);
--color-thv05-amber: #f59e0b;
--color-thv05-amber: oklch(0.77 0.16 70);
--font-thv05-sans: 'Space Grotesk', system-ui, sans-serif;
}
.thv-05 {
width: 100%;
min-height: 100svh;
}
.thv-05 ::selection {
background: var(--color-thv05-cyan);
color: #fff;
}@import "tailwindcss";
@theme {
--color-thv05-bg: #020917;
--color-thv05-bg: oklch(0.13 0.03 260);
--color-thv05-dim: #475569;
--color-thv05-dim: oklch(0.45 0.03 257);
--color-thv05-cyan: #06b6d4;
--color-thv05-cyan: oklch(0.71 0.14 208);
--color-thv05-rose: #f43f5e;
--color-thv05-rose: oklch(0.65 0.23 16);
--color-thv05-violet: #8b5cf6;
--color-thv05-violet: oklch(0.63 0.21 295);
--color-thv05-amber: #f59e0b;
--color-thv05-amber: oklch(0.77 0.16 70);
--font-thv05-sans: 'Space Grotesk', system-ui, sans-serif;
}
.thv-05 {
width: 100%;
min-height: 100svh;
}
.thv-05 ::selection {
background: var(--color-thv05-cyan);
color: #fff;
}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-thv05-bg: #020917;
--color-thv05-bg: oklch(0.13 0.03 260);
--color-thv05-dim: #475569;
--color-thv05-dim: oklch(0.45 0.03 257);
--color-thv05-cyan: #06b6d4;
--color-thv05-cyan: oklch(0.71 0.14 208);
--color-thv05-rose: #f43f5e;
--color-thv05-rose: oklch(0.65 0.23 16);
--color-thv05-violet: #8b5cf6;
--color-thv05-violet: oklch(0.63 0.21 295);
--color-thv05-amber: #f59e0b;
--color-thv05-amber: oklch(0.77 0.16 70);
--font-thv05-sans: 'Space Grotesk', system-ui, sans-serif;
}
.thv-05 {
width: 100%;
min-height: 100svh;
}
.thv-05 ::selection {
background: var(--color-thv05-cyan);
color: #fff;
}@import "tailwindcss";
@theme {
--color-thv05-bg: #020917;
--color-thv05-bg: oklch(0.13 0.03 260);
--color-thv05-dim: #475569;
--color-thv05-dim: oklch(0.45 0.03 257);
--color-thv05-cyan: #06b6d4;
--color-thv05-cyan: oklch(0.71 0.14 208);
--color-thv05-rose: #f43f5e;
--color-thv05-rose: oklch(0.65 0.23 16);
--color-thv05-violet: #8b5cf6;
--color-thv05-violet: oklch(0.63 0.21 295);
--color-thv05-amber: #f59e0b;
--color-thv05-amber: oklch(0.77 0.16 70);
--font-thv05-sans: 'Space Grotesk', system-ui, sans-serif;
}
.thv-05 {
width: 100%;
min-height: 100svh;
}
.thv-05 ::selection {
background: var(--color-thv05-cyan);
color: #fff;
}<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&family=JetBrains+Mono:wght@400;500;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=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;800&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Sixteen CSS rules become four class lists. The clip and the translate that depends on it are now two tokens apart instead of twenty lines.
- UI change from the CSS original: the words are focusable and split on
focus-visible:; the fade variant drops the rotation under keyboard focus so the word does not leave the ring. - Em-based arbitrary translates (
-translate-y-[0.55em]) are the detail that makes the port responsive — the source's.55emwas already right, and Tailwind carries it verbatim.
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 Split Text Hover Effect
Source: https://codefronts.com/motion/tailwind-css-hover-effects/tailwind-split-text-hover-effect/
Four hovers that cut a word in half and push the pieces apart — top/bottom, left/right, a diagonal polygon and a split-plus-fade. Two clipped copies of the text live in before: and after:; the base element goes transparent so nothing shows through the seam.
## HTML
```html
<section class="thv-05 w-full min-h-svh grid place-items-center bg-thv05-bg font-thv05-sans px-6 py-14">
<div class="w-[min(820px,100%)] grid grid-cols-[repeat(auto-fit,minmax(260px,1fr))] gap-10">
<div class="flex flex-col items-center gap-6 px-8 py-14 rounded-2xl bg-white/[0.025] border border-white/[0.07]">
<span tabindex="0" data-text="DIVIDE" class="relative inline-block overflow-hidden text-[clamp(2rem,6vw,3rem)] font-black tracking-[0.1em] text-transparent rounded-xs focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-thv05-cyan before:content-[attr(data-text)] before:absolute before:inset-0 before:text-thv05-cyan before:[clip-path:inset(0_0_50%_0)] before:transition-transform before:duration-450 before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[attr(data-text)] after:absolute after:inset-0 after:text-thv05-cyan after:[clip-path:inset(50%_0_0_0)] after:transition-transform after:duration-450 after:ease-[cubic-bezier(.4,0,.2,1)] hover:before:-translate-y-[0.55em] hover:after:translate-y-[0.55em] focus-visible:before:-translate-y-[0.55em] focus-visible:after:translate-y-[0.55em] motion-reduce:before:transition-none motion-reduce:after:transition-none">DIVIDE</span>
<span class="text-[11px] tracking-[0.15em] uppercase text-thv05-dim">top / bottom split</span>
</div>
<div class="flex flex-col items-center gap-6 px-8 py-14 rounded-2xl bg-white/[0.025] border border-white/[0.07]">
<span tabindex="0" data-text="SCHISM" class="relative inline-block overflow-hidden text-[clamp(2rem,6vw,3rem)] font-black tracking-[0.1em] text-transparent rounded-xs focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-thv05-rose before:content-[attr(data-text)] before:absolute before:inset-0 before:text-thv05-rose before:[clip-path:inset(0_50%_0_0)] before:transition-transform before:duration-450 before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[attr(data-text)] after:absolute after:inset-0 after:text-thv05-amber after:[clip-path:inset(0_0_0_50%)] after:transition-transform after:duration-450 after:ease-[cubic-bezier(.4,0,.2,1)] hover:before:-translate-x-[0.4em] hover:after:translate-x-[0.4em] focus-visible:before:-translate-x-[0.4em] focus-visible:after:translate-x-[0.4em] motion-reduce:before:transition-none motion-reduce:after:transition-none">SCHISM</span>
<span class="text-[11px] tracking-[0.15em] uppercase text-thv05-dim">left / right push</span>
</div>
<div class="flex flex-col items-center gap-6 px-8 py-14 rounded-2xl bg-white/[0.025] border border-white/[0.07]">
<span tabindex="0" data-text="SLASH" class="relative inline-block text-[clamp(2rem,6vw,3rem)] font-black tracking-[0.1em] text-transparent rounded-xs focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-thv05-violet before:content-[attr(data-text)] before:absolute before:inset-0 before:text-thv05-violet before:[clip-path:polygon(0_0,100%_0,60%_100%,0_100%)] before:transition-transform before:duration-450 before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[attr(data-text)] after:absolute after:inset-0 after:text-thv05-cyan after:[clip-path:polygon(60%_0,100%_0,100%_100%,0_100%)] after:transition-transform after:duration-450 after:ease-[cubic-bezier(.4,0,.2,1)] hover:before:-translate-x-[0.3em] hover:before:-translate-y-[0.3em] hover:after:translate-x-[0.3em] hover:after:translate-y-[0.3em] focus-visible:before:-translate-x-[0.3em] focus-visible:before:-translate-y-[0.3em] focus-visible:after:translate-x-[0.3em] focus-visible:after:translate-y-[0.3em] motion-reduce:before:transition-none motion-reduce:after:transition-none">SLASH</span>
<span class="text-[11px] tracking-[0.15em] uppercase text-thv05-dim">diagonal polygon</span>
</div>
<div class="flex flex-col items-center gap-6 px-8 py-14 rounded-2xl bg-white/[0.025] border border-white/[0.07]">
<span tabindex="0" data-text="RIFT" class="relative inline-block text-[clamp(2rem,6vw,3rem)] font-black tracking-[0.1em] text-transparent rounded-xs focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-thv05-amber before:content-[attr(data-text)] before:absolute before:inset-0 before:text-thv05-amber before:[clip-path:inset(0_0_50%_0)] before:transition-[transform,opacity] before:duration-450 before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[attr(data-text)] after:absolute after:inset-0 after:text-thv05-rose after:[clip-path:inset(50%_0_0_0)] after:transition-[transform,opacity] after:duration-450 after:ease-[cubic-bezier(.4,0,.2,1)] hover:before:-translate-y-[0.6em] hover:before:-rotate-3 hover:before:opacity-0 hover:after:translate-y-[0.6em] hover:after:rotate-3 hover:after:opacity-0 focus-visible:before:-translate-y-[0.6em] focus-visible:before:opacity-0 focus-visible:after:translate-y-[0.6em] focus-visible:after:opacity-0 motion-reduce:before:transition-none motion-reduce:after:transition-none">RIFT</span>
<span class="text-[11px] tracking-[0.15em] uppercase text-thv05-dim">split + fade</span>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-thv05-bg: #020917;
--color-thv05-bg: oklch(0.13 0.03 260);
--color-thv05-dim: #475569;
--color-thv05-dim: oklch(0.45 0.03 257);
--color-thv05-cyan: #06b6d4;
--color-thv05-cyan: oklch(0.71 0.14 208);
--color-thv05-rose: #f43f5e;
--color-thv05-rose: oklch(0.65 0.23 16);
--color-thv05-violet: #8b5cf6;
--color-thv05-violet: oklch(0.63 0.21 295);
--color-thv05-amber: #f59e0b;
--color-thv05-amber: oklch(0.77 0.16 70);
--font-thv05-sans: 'Space Grotesk', system-ui, sans-serif;
}
.thv-05 {
width: 100%;
min-height: 100svh;
}
.thv-05 ::selection {
background: var(--color-thv05-cyan);
color: #fff;
}
```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 Split Text Hover Effect
Source: https://codefronts.com/motion/tailwind-css-hover-effects/tailwind-split-text-hover-effect/
Four hovers that cut a word in half and push the pieces apart — top/bottom, left/right, a diagonal polygon and a split-plus-fade. Two clipped copies of the text live in before: and after:; the base element goes transparent so nothing shows through the seam.
## HTML
```html
<section class="thv-05 w-full min-h-svh grid place-items-center bg-thv05-bg font-thv05-sans px-6 py-14">
<div class="w-[min(820px,100%)] grid grid-cols-[repeat(auto-fit,minmax(260px,1fr))] gap-10">
<div class="flex flex-col items-center gap-6 px-8 py-14 rounded-2xl bg-white/[0.025] border border-white/[0.07]">
<span tabindex="0" data-text="DIVIDE" class="relative inline-block overflow-hidden text-[clamp(2rem,6vw,3rem)] font-black tracking-[0.1em] text-transparent rounded-xs focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-thv05-cyan before:content-[attr(data-text)] before:absolute before:inset-0 before:text-thv05-cyan before:[clip-path:inset(0_0_50%_0)] before:transition-transform before:duration-450 before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[attr(data-text)] after:absolute after:inset-0 after:text-thv05-cyan after:[clip-path:inset(50%_0_0_0)] after:transition-transform after:duration-450 after:ease-[cubic-bezier(.4,0,.2,1)] hover:before:-translate-y-[0.55em] hover:after:translate-y-[0.55em] focus-visible:before:-translate-y-[0.55em] focus-visible:after:translate-y-[0.55em] motion-reduce:before:transition-none motion-reduce:after:transition-none">DIVIDE</span>
<span class="text-[11px] tracking-[0.15em] uppercase text-thv05-dim">top / bottom split</span>
</div>
<div class="flex flex-col items-center gap-6 px-8 py-14 rounded-2xl bg-white/[0.025] border border-white/[0.07]">
<span tabindex="0" data-text="SCHISM" class="relative inline-block overflow-hidden text-[clamp(2rem,6vw,3rem)] font-black tracking-[0.1em] text-transparent rounded-xs focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-thv05-rose before:content-[attr(data-text)] before:absolute before:inset-0 before:text-thv05-rose before:[clip-path:inset(0_50%_0_0)] before:transition-transform before:duration-450 before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[attr(data-text)] after:absolute after:inset-0 after:text-thv05-amber after:[clip-path:inset(0_0_0_50%)] after:transition-transform after:duration-450 after:ease-[cubic-bezier(.4,0,.2,1)] hover:before:-translate-x-[0.4em] hover:after:translate-x-[0.4em] focus-visible:before:-translate-x-[0.4em] focus-visible:after:translate-x-[0.4em] motion-reduce:before:transition-none motion-reduce:after:transition-none">SCHISM</span>
<span class="text-[11px] tracking-[0.15em] uppercase text-thv05-dim">left / right push</span>
</div>
<div class="flex flex-col items-center gap-6 px-8 py-14 rounded-2xl bg-white/[0.025] border border-white/[0.07]">
<span tabindex="0" data-text="SLASH" class="relative inline-block text-[clamp(2rem,6vw,3rem)] font-black tracking-[0.1em] text-transparent rounded-xs focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-thv05-violet before:content-[attr(data-text)] before:absolute before:inset-0 before:text-thv05-violet before:[clip-path:polygon(0_0,100%_0,60%_100%,0_100%)] before:transition-transform before:duration-450 before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[attr(data-text)] after:absolute after:inset-0 after:text-thv05-cyan after:[clip-path:polygon(60%_0,100%_0,100%_100%,0_100%)] after:transition-transform after:duration-450 after:ease-[cubic-bezier(.4,0,.2,1)] hover:before:-translate-x-[0.3em] hover:before:-translate-y-[0.3em] hover:after:translate-x-[0.3em] hover:after:translate-y-[0.3em] focus-visible:before:-translate-x-[0.3em] focus-visible:before:-translate-y-[0.3em] focus-visible:after:translate-x-[0.3em] focus-visible:after:translate-y-[0.3em] motion-reduce:before:transition-none motion-reduce:after:transition-none">SLASH</span>
<span class="text-[11px] tracking-[0.15em] uppercase text-thv05-dim">diagonal polygon</span>
</div>
<div class="flex flex-col items-center gap-6 px-8 py-14 rounded-2xl bg-white/[0.025] border border-white/[0.07]">
<span tabindex="0" data-text="RIFT" class="relative inline-block text-[clamp(2rem,6vw,3rem)] font-black tracking-[0.1em] text-transparent rounded-xs focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-thv05-amber before:content-[attr(data-text)] before:absolute before:inset-0 before:text-thv05-amber before:[clip-path:inset(0_0_50%_0)] before:transition-[transform,opacity] before:duration-450 before:ease-[cubic-bezier(.4,0,.2,1)] after:content-[attr(data-text)] after:absolute after:inset-0 after:text-thv05-rose after:[clip-path:inset(50%_0_0_0)] after:transition-[transform,opacity] after:duration-450 after:ease-[cubic-bezier(.4,0,.2,1)] hover:before:-translate-y-[0.6em] hover:before:-rotate-3 hover:before:opacity-0 hover:after:translate-y-[0.6em] hover:after:rotate-3 hover:after:opacity-0 focus-visible:before:-translate-y-[0.6em] focus-visible:before:opacity-0 focus-visible:after:translate-y-[0.6em] focus-visible:after:opacity-0 motion-reduce:before:transition-none motion-reduce:after:transition-none">RIFT</span>
<span class="text-[11px] tracking-[0.15em] uppercase text-thv05-dim">split + fade</span>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-thv05-bg: #020917;
--color-thv05-bg: oklch(0.13 0.03 260);
--color-thv05-dim: #475569;
--color-thv05-dim: oklch(0.45 0.03 257);
--color-thv05-cyan: #06b6d4;
--color-thv05-cyan: oklch(0.71 0.14 208);
--color-thv05-rose: #f43f5e;
--color-thv05-rose: oklch(0.65 0.23 16);
--color-thv05-violet: #8b5cf6;
--color-thv05-violet: oklch(0.63 0.21 295);
--color-thv05-amber: #f59e0b;
--color-thv05-amber: oklch(0.77 0.16 70);
--font-thv05-sans: 'Space Grotesk', system-ui, sans-serif;
}
.thv-05 {
width: 100%;
min-height: 100svh;
}
.thv-05 ::selection {
background: var(--color-thv05-cyan);
color: #fff;
}
```How this works
Two copies, two clips, one class list. Both pseudo-elements read content-[attr(data-text)] and sit at absolute inset-0; the only difference between them is the clip and the hover translate. In CSS that was four rules per variant, two of them far below the others.
Em translates keep the split proportional. hover:before:-translate-y-[0.55em] moves by a share of the font size, so the effect survives the clamp() type scale unchanged — a px translate would look right at one size only.
The base text stays readable to machines. text-transparent hides the original glyphs behind the two halves without removing them, so assistive technology still announces the word once, not twice, and never announces the pseudo-element copies.
Make it yours
- Deeper split:
hover:before:-translate-y-[1.2em]and its mirror. - Two-tone the halves by giving
before:text-*andafter:text-*different tokens (the left/right variant already does). - Steepen the diagonal by editing the polygon's middle stops.
Gotchas — read before shipping
- Keep
overflow-hiddenon the word if the halves should disappear at the boundary rather than float over the card. - Without
text-transparenton the host, the un-split original shows through the gap. clip-pathandwill-change:transformon the same element can defeat the clip in some engines — don't add one here.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
clip-path: inset() and polygon() are Baseline; the floor is Tailwind v4's generated sheet.