20 / 33
All 33 in 33 Tailwind CSS Card Hover Effects
- 01Tailwind 3D Tilt & Parallax Card with Depth Layers
- 02Tailwind Glowing Gradient & Glassmorphic Border Card
- 03Tailwind Image Zoom Card with Slide-Up Content Reveal
- 04Tailwind 3D Flip Card (Front to Back) with backface-visibility
- 05Tailwind Sibling De-emphasis Pricing Grid with has-hover
- 06Tailwind Minimalist Elevation Card with Layered Dynamic Shadows
- 07Tailwind Elastic Lift Card with Spring Cubic-Bezier
- 08Tailwind Animated Conic Gradient Border Card with @property
- 09Tailwind Stacked Layer Reveal Card (Fanned Risograph Deck)
- 10Tailwind Holographic Foil Shimmer Card with mix-blend-mode
- 11Tailwind Curtain Reveal Card (Parting Velvet Halves)
- 12Tailwind Corner Peel Card with clip-path Triangles
- 13Tailwind Neon Sign Card with Layered Text-Shadow Glow
- 14Tailwind CRT Scanline Terminal Card with Sweeping Beam
- 15Tailwind Depth Parallax Landscape Card with Layer Multipliers
- 16Tailwind Cursor Spotlight Card with Masked Border Highlight
- 17Tailwind 3D Tilt Product Card with Specular Glare
- 18Tailwind Slide-Up Panel Card with Peek Header Swap
- 19Tailwind Credit Card Flip with Magnetic Strip & CVV
- 20Tailwind RGB Channel-Split Glitch Card
- 21Tailwind Rotating Conic Aura Card with Blurred Gradient Bloom
- 22Tailwind Blob-to-Rectangle Border Morph Card
- 23Tailwind Magnetic Float Orb Card with Pointer Attraction
- 24Tailwind Typewriter Reveal Card with Steps Animation & Caret
- 25Tailwind Magazine Overlay Slide Card (Full-Face Panel Takeover)
- 26Tailwind Breathing Film Grain Card with SVG Turbulence Noise
- 27Tailwind Frosted Glass Intensify Card with Deepening Backdrop Blur
- 28Tailwind Blueprint Reveal Card with Circular clip-path Wipe
- 29Tailwind Cursor-Anchored Shockwave Ripple Card
- 30Tailwind Color-Burn Ignition Card with Grayscale-to-Fire Hover
- 31Tailwind Prismatic Light Sweep Card with Screen Blend Bar
- 32Tailwind Book Cover Open Card with Hinged 3D Rotation
- 33Tailwind Drifting Aurora Ribbons Card with Blurred Gradient Bands
Tailwind RGB Channel-Split Glitch Card
Three stacked copies of one headline — red channel, cyan channel, clean white — desyncing into datamoshed slices on hover, with two drifting scan bars. The port keeps all three keyframe sets in CSS (they animate clip-path, which no utility can tween) and applies them from the markup.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tch-20 w-full min-h-svh grid place-items-center bg-tch20-bg font-tch20-mono p-8">
<article tabindex="0" class="group relative w-[340px] h-[440px] rounded overflow-hidden cursor-pointer outline-none bg-tch20-card border border-tch20-edge focus-visible:outline-2 focus-visible:outline-tch20-cyan focus-visible:outline-offset-4">
<div class="absolute inset-0 z-3 pointer-events-none opacity-0 group-hover:opacity-100 group-hover:animate-tch20-bars group-focus-visible:opacity-100 motion-reduce:animate-none motion-reduce:group-hover:animate-none before:content-[''] before:absolute before:inset-x-0 before:top-[32%] before:h-0.5 before:bg-tch20-cyan/50 after:content-[''] after:absolute after:inset-x-0 after:top-[68%] after:h-0.5 after:bg-tch20-red/50"></div>
<div class="relative z-2 h-full p-[2.4rem] flex flex-col justify-between text-tch20-ink">
<span class="text-[0.72rem] tracking-[0.25em] text-tch20-cyan">// SIGNAL_LOST</span>
<h2 class="relative inline-block font-tch20-display text-[4rem] leading-[0.85] uppercase text-white">
<span aria-hidden="true" class="absolute inset-0 size-full pointer-events-none text-tch20-red opacity-0 transition-opacity duration-200 group-hover:opacity-85 group-hover:animate-tch20-shift-a group-focus-visible:opacity-85 motion-reduce:animate-none motion-reduce:group-hover:animate-none">NULL<br>CITY</span>
<span aria-hidden="true" class="absolute inset-0 size-full pointer-events-none text-tch20-cyan opacity-0 transition-opacity duration-200 group-hover:opacity-85 group-hover:animate-tch20-shift-b group-focus-visible:opacity-85 motion-reduce:animate-none motion-reduce:group-hover:animate-none">NULL<br>CITY</span>
<span class="relative z-1 block">NULL<br>CITY</span>
</h2>
<p class="text-[0.82rem] leading-[1.6] text-tch20-body">A cyberpunk audio-visual set. Corrupted frames, datamoshed visuals, bass you feel in your teeth.</p>
<span class="text-[0.72rem] tracking-[0.15em] text-tch20-mut">FRI · WAREHOUSE 7 · 23:00</span>
</div>
</article>
</section><section class="tch-20 w-full min-h-svh grid place-items-center bg-tch20-bg font-tch20-mono p-8">
<article tabindex="0" class="group relative w-[340px] h-[440px] rounded overflow-hidden cursor-pointer outline-none bg-tch20-card border border-tch20-edge focus-visible:outline-2 focus-visible:outline-tch20-cyan focus-visible:outline-offset-4">
<div class="absolute inset-0 z-3 pointer-events-none opacity-0 group-hover:opacity-100 group-hover:animate-tch20-bars group-focus-visible:opacity-100 motion-reduce:animate-none motion-reduce:group-hover:animate-none before:content-[''] before:absolute before:inset-x-0 before:top-[32%] before:h-0.5 before:bg-tch20-cyan/50 after:content-[''] after:absolute after:inset-x-0 after:top-[68%] after:h-0.5 after:bg-tch20-red/50"></div>
<div class="relative z-2 h-full p-[2.4rem] flex flex-col justify-between text-tch20-ink">
<span class="text-[0.72rem] tracking-[0.25em] text-tch20-cyan">// SIGNAL_LOST</span>
<h2 class="relative inline-block font-tch20-display text-[4rem] leading-[0.85] uppercase text-white">
<span aria-hidden="true" class="absolute inset-0 size-full pointer-events-none text-tch20-red opacity-0 transition-opacity duration-200 group-hover:opacity-85 group-hover:animate-tch20-shift-a group-focus-visible:opacity-85 motion-reduce:animate-none motion-reduce:group-hover:animate-none">NULL<br>CITY</span>
<span aria-hidden="true" class="absolute inset-0 size-full pointer-events-none text-tch20-cyan opacity-0 transition-opacity duration-200 group-hover:opacity-85 group-hover:animate-tch20-shift-b group-focus-visible:opacity-85 motion-reduce:animate-none motion-reduce:group-hover:animate-none">NULL<br>CITY</span>
<span class="relative z-1 block">NULL<br>CITY</span>
</h2>
<p class="text-[0.82rem] leading-[1.6] text-tch20-body">A cyberpunk audio-visual set. Corrupted frames, datamoshed visuals, bass you feel in your teeth.</p>
<span class="text-[0.72rem] tracking-[0.15em] text-tch20-mut">FRI · WAREHOUSE 7 · 23:00</span>
</div>
</article>
</section>@import "tailwindcss";
@keyframes tch20-shift-a {
0% {
transform: translate(0,0);
clip-path: inset(0 0 70% 0);
}
50% {
transform: translate(-4px,2px);
clip-path: inset(40% 0 20% 0);
}
100% {
transform: translate(3px,-2px);
clip-path: inset(70% 0 0 0);
}
}
@keyframes tch20-shift-b {
0% {
transform: translate(0,0);
clip-path: inset(60% 0 10% 0);
}
50% {
transform: translate(4px,-2px);
clip-path: inset(10% 0 60% 0);
}
100% {
transform: translate(-3px,2px);
clip-path: inset(30% 0 40% 0);
}
}
@keyframes tch20-bars {
0%,
100% {
transform: translateX(0);
}
50% {
transform: translateX(6px);
}
}
@theme {
--color-tch20-bg: #08080a;
--color-tch20-bg: oklch(0.1 0.003 285);
--color-tch20-card: #0f0f12;
--color-tch20-card: oklch(0.14 0.005 285);
--color-tch20-edge: #1c1c22;
--color-tch20-edge: oklch(0.21 0.007 285);
--color-tch20-ink: #e8e8ee;
--color-tch20-ink: oklch(0.92 0.004 285);
--color-tch20-body: #888888;
--color-tch20-body: oklch(0.62 0 0);
--color-tch20-mut: #555555;
--color-tch20-mut: oklch(0.44 0 0);
--color-tch20-red: #ff003c;
--color-tch20-red: oklch(0.6 0.28 20);
--color-tch20-cyan: #00e0ff;
--color-tch20-cyan: oklch(0.83 0.14 205);
--font-tch20-mono: 'Space Mono', ui-monospace, monospace;
--font-tch20-display: 'Anton', system-ui, sans-serif;
--animate-tch20-shift-a: tch20-shift-a 0.35s steps(2) infinite;
--animate-tch20-shift-b: tch20-shift-b 0.35s steps(2) infinite;
--animate-tch20-bars: tch20-bars 0.4s steps(3) infinite;
}
.tch-20 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@keyframes tch20-shift-a {
0% {
transform: translate(0,0);
clip-path: inset(0 0 70% 0);
}
50% {
transform: translate(-4px,2px);
clip-path: inset(40% 0 20% 0);
}
100% {
transform: translate(3px,-2px);
clip-path: inset(70% 0 0 0);
}
}
@keyframes tch20-shift-b {
0% {
transform: translate(0,0);
clip-path: inset(60% 0 10% 0);
}
50% {
transform: translate(4px,-2px);
clip-path: inset(10% 0 60% 0);
}
100% {
transform: translate(-3px,2px);
clip-path: inset(30% 0 40% 0);
}
}
@keyframes tch20-bars {
0%,
100% {
transform: translateX(0);
}
50% {
transform: translateX(6px);
}
}
@theme {
--color-tch20-bg: #08080a;
--color-tch20-bg: oklch(0.1 0.003 285);
--color-tch20-card: #0f0f12;
--color-tch20-card: oklch(0.14 0.005 285);
--color-tch20-edge: #1c1c22;
--color-tch20-edge: oklch(0.21 0.007 285);
--color-tch20-ink: #e8e8ee;
--color-tch20-ink: oklch(0.92 0.004 285);
--color-tch20-body: #888888;
--color-tch20-body: oklch(0.62 0 0);
--color-tch20-mut: #555555;
--color-tch20-mut: oklch(0.44 0 0);
--color-tch20-red: #ff003c;
--color-tch20-red: oklch(0.6 0.28 20);
--color-tch20-cyan: #00e0ff;
--color-tch20-cyan: oklch(0.83 0.14 205);
--font-tch20-mono: 'Space Mono', ui-monospace, monospace;
--font-tch20-display: 'Anton', system-ui, sans-serif;
--animate-tch20-shift-a: tch20-shift-a 0.35s steps(2) infinite;
--animate-tch20-shift-b: tch20-shift-b 0.35s steps(2) infinite;
--animate-tch20-bars: tch20-bars 0.4s steps(3) infinite;
}
.tch-20 {
width: 100%;
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";
@keyframes tch20-shift-a {
0% {
transform: translate(0,0);
clip-path: inset(0 0 70% 0);
}
50% {
transform: translate(-4px,2px);
clip-path: inset(40% 0 20% 0);
}
100% {
transform: translate(3px,-2px);
clip-path: inset(70% 0 0 0);
}
}
@keyframes tch20-shift-b {
0% {
transform: translate(0,0);
clip-path: inset(60% 0 10% 0);
}
50% {
transform: translate(4px,-2px);
clip-path: inset(10% 0 60% 0);
}
100% {
transform: translate(-3px,2px);
clip-path: inset(30% 0 40% 0);
}
}
@keyframes tch20-bars {
0%,
100% {
transform: translateX(0);
}
50% {
transform: translateX(6px);
}
}
@theme {
--color-tch20-bg: #08080a;
--color-tch20-bg: oklch(0.1 0.003 285);
--color-tch20-card: #0f0f12;
--color-tch20-card: oklch(0.14 0.005 285);
--color-tch20-edge: #1c1c22;
--color-tch20-edge: oklch(0.21 0.007 285);
--color-tch20-ink: #e8e8ee;
--color-tch20-ink: oklch(0.92 0.004 285);
--color-tch20-body: #888888;
--color-tch20-body: oklch(0.62 0 0);
--color-tch20-mut: #555555;
--color-tch20-mut: oklch(0.44 0 0);
--color-tch20-red: #ff003c;
--color-tch20-red: oklch(0.6 0.28 20);
--color-tch20-cyan: #00e0ff;
--color-tch20-cyan: oklch(0.83 0.14 205);
--font-tch20-mono: 'Space Mono', ui-monospace, monospace;
--font-tch20-display: 'Anton', system-ui, sans-serif;
--animate-tch20-shift-a: tch20-shift-a 0.35s steps(2) infinite;
--animate-tch20-shift-b: tch20-shift-b 0.35s steps(2) infinite;
--animate-tch20-bars: tch20-bars 0.4s steps(3) infinite;
}
.tch-20 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@keyframes tch20-shift-a {
0% {
transform: translate(0,0);
clip-path: inset(0 0 70% 0);
}
50% {
transform: translate(-4px,2px);
clip-path: inset(40% 0 20% 0);
}
100% {
transform: translate(3px,-2px);
clip-path: inset(70% 0 0 0);
}
}
@keyframes tch20-shift-b {
0% {
transform: translate(0,0);
clip-path: inset(60% 0 10% 0);
}
50% {
transform: translate(4px,-2px);
clip-path: inset(10% 0 60% 0);
}
100% {
transform: translate(-3px,2px);
clip-path: inset(30% 0 40% 0);
}
}
@keyframes tch20-bars {
0%,
100% {
transform: translateX(0);
}
50% {
transform: translateX(6px);
}
}
@theme {
--color-tch20-bg: #08080a;
--color-tch20-bg: oklch(0.1 0.003 285);
--color-tch20-card: #0f0f12;
--color-tch20-card: oklch(0.14 0.005 285);
--color-tch20-edge: #1c1c22;
--color-tch20-edge: oklch(0.21 0.007 285);
--color-tch20-ink: #e8e8ee;
--color-tch20-ink: oklch(0.92 0.004 285);
--color-tch20-body: #888888;
--color-tch20-body: oklch(0.62 0 0);
--color-tch20-mut: #555555;
--color-tch20-mut: oklch(0.44 0 0);
--color-tch20-red: #ff003c;
--color-tch20-red: oklch(0.6 0.28 20);
--color-tch20-cyan: #00e0ff;
--color-tch20-cyan: oklch(0.83 0.14 205);
--font-tch20-mono: 'Space Mono', ui-monospace, monospace;
--font-tch20-display: 'Anton', system-ui, sans-serif;
--animate-tch20-shift-a: tch20-shift-a 0.35s steps(2) infinite;
--animate-tch20-shift-b: tch20-shift-b 0.35s steps(2) infinite;
--animate-tch20-bars: tch20-bars 0.4s steps(3) infinite;
}
.tch-20 {
width: 100%;
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=Anton&family=Space+Mono:wght@400;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=Anton&family=Space+Mono:wght@400;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Three
--animate-*entries and three keyframe blocks — the highest CSS-to-utility ratio in the collection, becauseclip-pathkeyframes are simply not utility-shaped. - The scan bars ride on one element's
before:andafter:, so two hairlines cost zero extra DOM. - UI change from the CSS original: focusable card, glitch fires on
group-focus-visible:, and the scan-bar element now carriespointer-events-none(the original left it clickable).
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 RGB Channel-Split Glitch Card
Source: https://codefronts.com/motion/tailwind-css-card-hover-effects/tailwind-rgb-glitch-card-hover-effect/
Three stacked copies of one headline — red channel, cyan channel, clean white — desyncing into datamoshed slices on hover, with two drifting scan bars. The port keeps all three keyframe sets in CSS (they animate clip-path, which no utility can tween) and applies them from the markup.
## HTML
```html
<section class="tch-20 w-full min-h-svh grid place-items-center bg-tch20-bg font-tch20-mono p-8">
<article tabindex="0" class="group relative w-[340px] h-[440px] rounded overflow-hidden cursor-pointer outline-none bg-tch20-card border border-tch20-edge focus-visible:outline-2 focus-visible:outline-tch20-cyan focus-visible:outline-offset-4">
<div class="absolute inset-0 z-3 pointer-events-none opacity-0 group-hover:opacity-100 group-hover:animate-tch20-bars group-focus-visible:opacity-100 motion-reduce:animate-none motion-reduce:group-hover:animate-none before:content-[''] before:absolute before:inset-x-0 before:top-[32%] before:h-0.5 before:bg-tch20-cyan/50 after:content-[''] after:absolute after:inset-x-0 after:top-[68%] after:h-0.5 after:bg-tch20-red/50"></div>
<div class="relative z-2 h-full p-[2.4rem] flex flex-col justify-between text-tch20-ink">
<span class="text-[0.72rem] tracking-[0.25em] text-tch20-cyan">// SIGNAL_LOST</span>
<h2 class="relative inline-block font-tch20-display text-[4rem] leading-[0.85] uppercase text-white">
<span aria-hidden="true" class="absolute inset-0 size-full pointer-events-none text-tch20-red opacity-0 transition-opacity duration-200 group-hover:opacity-85 group-hover:animate-tch20-shift-a group-focus-visible:opacity-85 motion-reduce:animate-none motion-reduce:group-hover:animate-none">NULL<br>CITY</span>
<span aria-hidden="true" class="absolute inset-0 size-full pointer-events-none text-tch20-cyan opacity-0 transition-opacity duration-200 group-hover:opacity-85 group-hover:animate-tch20-shift-b group-focus-visible:opacity-85 motion-reduce:animate-none motion-reduce:group-hover:animate-none">NULL<br>CITY</span>
<span class="relative z-1 block">NULL<br>CITY</span>
</h2>
<p class="text-[0.82rem] leading-[1.6] text-tch20-body">A cyberpunk audio-visual set. Corrupted frames, datamoshed visuals, bass you feel in your teeth.</p>
<span class="text-[0.72rem] tracking-[0.15em] text-tch20-mut">FRI · WAREHOUSE 7 · 23:00</span>
</div>
</article>
</section>
```
## CSS
```css
@import "tailwindcss";
@keyframes tch20-shift-a {
0% {
transform: translate(0,0);
clip-path: inset(0 0 70% 0);
}
50% {
transform: translate(-4px,2px);
clip-path: inset(40% 0 20% 0);
}
100% {
transform: translate(3px,-2px);
clip-path: inset(70% 0 0 0);
}
}
@keyframes tch20-shift-b {
0% {
transform: translate(0,0);
clip-path: inset(60% 0 10% 0);
}
50% {
transform: translate(4px,-2px);
clip-path: inset(10% 0 60% 0);
}
100% {
transform: translate(-3px,2px);
clip-path: inset(30% 0 40% 0);
}
}
@keyframes tch20-bars {
0%,
100% {
transform: translateX(0);
}
50% {
transform: translateX(6px);
}
}
@theme {
--color-tch20-bg: #08080a;
--color-tch20-bg: oklch(0.1 0.003 285);
--color-tch20-card: #0f0f12;
--color-tch20-card: oklch(0.14 0.005 285);
--color-tch20-edge: #1c1c22;
--color-tch20-edge: oklch(0.21 0.007 285);
--color-tch20-ink: #e8e8ee;
--color-tch20-ink: oklch(0.92 0.004 285);
--color-tch20-body: #888888;
--color-tch20-body: oklch(0.62 0 0);
--color-tch20-mut: #555555;
--color-tch20-mut: oklch(0.44 0 0);
--color-tch20-red: #ff003c;
--color-tch20-red: oklch(0.6 0.28 20);
--color-tch20-cyan: #00e0ff;
--color-tch20-cyan: oklch(0.83 0.14 205);
--font-tch20-mono: 'Space Mono', ui-monospace, monospace;
--font-tch20-display: 'Anton', system-ui, sans-serif;
--animate-tch20-shift-a: tch20-shift-a 0.35s steps(2) infinite;
--animate-tch20-shift-b: tch20-shift-b 0.35s steps(2) infinite;
--animate-tch20-bars: tch20-bars 0.4s steps(3) infinite;
}
.tch-20 {
width: 100%;
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 RGB Channel-Split Glitch Card
Source: https://codefronts.com/motion/tailwind-css-card-hover-effects/tailwind-rgb-glitch-card-hover-effect/
Three stacked copies of one headline — red channel, cyan channel, clean white — desyncing into datamoshed slices on hover, with two drifting scan bars. The port keeps all three keyframe sets in CSS (they animate clip-path, which no utility can tween) and applies them from the markup.
## HTML
```html
<section class="tch-20 w-full min-h-svh grid place-items-center bg-tch20-bg font-tch20-mono p-8">
<article tabindex="0" class="group relative w-[340px] h-[440px] rounded overflow-hidden cursor-pointer outline-none bg-tch20-card border border-tch20-edge focus-visible:outline-2 focus-visible:outline-tch20-cyan focus-visible:outline-offset-4">
<div class="absolute inset-0 z-3 pointer-events-none opacity-0 group-hover:opacity-100 group-hover:animate-tch20-bars group-focus-visible:opacity-100 motion-reduce:animate-none motion-reduce:group-hover:animate-none before:content-[''] before:absolute before:inset-x-0 before:top-[32%] before:h-0.5 before:bg-tch20-cyan/50 after:content-[''] after:absolute after:inset-x-0 after:top-[68%] after:h-0.5 after:bg-tch20-red/50"></div>
<div class="relative z-2 h-full p-[2.4rem] flex flex-col justify-between text-tch20-ink">
<span class="text-[0.72rem] tracking-[0.25em] text-tch20-cyan">// SIGNAL_LOST</span>
<h2 class="relative inline-block font-tch20-display text-[4rem] leading-[0.85] uppercase text-white">
<span aria-hidden="true" class="absolute inset-0 size-full pointer-events-none text-tch20-red opacity-0 transition-opacity duration-200 group-hover:opacity-85 group-hover:animate-tch20-shift-a group-focus-visible:opacity-85 motion-reduce:animate-none motion-reduce:group-hover:animate-none">NULL<br>CITY</span>
<span aria-hidden="true" class="absolute inset-0 size-full pointer-events-none text-tch20-cyan opacity-0 transition-opacity duration-200 group-hover:opacity-85 group-hover:animate-tch20-shift-b group-focus-visible:opacity-85 motion-reduce:animate-none motion-reduce:group-hover:animate-none">NULL<br>CITY</span>
<span class="relative z-1 block">NULL<br>CITY</span>
</h2>
<p class="text-[0.82rem] leading-[1.6] text-tch20-body">A cyberpunk audio-visual set. Corrupted frames, datamoshed visuals, bass you feel in your teeth.</p>
<span class="text-[0.72rem] tracking-[0.15em] text-tch20-mut">FRI · WAREHOUSE 7 · 23:00</span>
</div>
</article>
</section>
```
## CSS
```css
@import "tailwindcss";
@keyframes tch20-shift-a {
0% {
transform: translate(0,0);
clip-path: inset(0 0 70% 0);
}
50% {
transform: translate(-4px,2px);
clip-path: inset(40% 0 20% 0);
}
100% {
transform: translate(3px,-2px);
clip-path: inset(70% 0 0 0);
}
}
@keyframes tch20-shift-b {
0% {
transform: translate(0,0);
clip-path: inset(60% 0 10% 0);
}
50% {
transform: translate(4px,-2px);
clip-path: inset(10% 0 60% 0);
}
100% {
transform: translate(-3px,2px);
clip-path: inset(30% 0 40% 0);
}
}
@keyframes tch20-bars {
0%,
100% {
transform: translateX(0);
}
50% {
transform: translateX(6px);
}
}
@theme {
--color-tch20-bg: #08080a;
--color-tch20-bg: oklch(0.1 0.003 285);
--color-tch20-card: #0f0f12;
--color-tch20-card: oklch(0.14 0.005 285);
--color-tch20-edge: #1c1c22;
--color-tch20-edge: oklch(0.21 0.007 285);
--color-tch20-ink: #e8e8ee;
--color-tch20-ink: oklch(0.92 0.004 285);
--color-tch20-body: #888888;
--color-tch20-body: oklch(0.62 0 0);
--color-tch20-mut: #555555;
--color-tch20-mut: oklch(0.44 0 0);
--color-tch20-red: #ff003c;
--color-tch20-red: oklch(0.6 0.28 20);
--color-tch20-cyan: #00e0ff;
--color-tch20-cyan: oklch(0.83 0.14 205);
--font-tch20-mono: 'Space Mono', ui-monospace, monospace;
--font-tch20-display: 'Anton', system-ui, sans-serif;
--animate-tch20-shift-a: tch20-shift-a 0.35s steps(2) infinite;
--animate-tch20-shift-b: tch20-shift-b 0.35s steps(2) infinite;
--animate-tch20-bars: tch20-bars 0.4s steps(3) infinite;
}
.tch-20 {
width: 100%;
min-height: 100svh;
}
```How this works
Three layers, one string. The two ghosts are absolutely positioned over the real text and tinted red and cyan. At rest they are fully transparent, so the headline is clean. On hover they fade to 0.85 and start their own clip-path: inset() animations, which slice each ghost into a different horizontal band per keyframe — the datamosh look.
steps() is the whole aesthetic. A smoothly interpolated channel split reads as a blur. Stepping between two or three discrete states reads as signal corruption, which is the point.
The ghosts are hidden from screen readers. aria-hidden="true" plus pointer-events-none: three visual copies, one announced string, zero click interference.
Make it yours
- Split distance: the
translatevalues inside the keyframes. 2px is a hint, 8px is heavy corruption. - Channel pair: red/cyan is the canonical CRT split; magenta/green reads more like tape damage.
- Delete the scan bars for a text-only glitch, or keep only the bars for a subtler card.
Gotchas — read before shipping
- Gate every animation behind
motion-reduce:. Glitch is a photosensitivity trigger, full stop. - Keep the ghosts
aria-hidden— three copies of a headline in the accessibility tree is a genuine usability failure. - The clean layer must stay at full opacity and meet contrast on its own; the ghosts are decoration, not text.
clip-pathkeyframes cannot be expressed as utilities, so the animation definitions stay in CSS — do not try to inline them.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Keyframes, clip-path: inset() and steps() timing are universally supported; the floor is Tailwind v4's generated sheet.