16 / 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 Spotlight Card Hover Effect
Four lighting treatments — centre spotlight, corner lamp, a travelling scan band and a vignette that lifts. Each is one gradient on a before: layer whose opacity (or position) changes on hover.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="thv-16 w-full min-h-svh grid place-items-center bg-thv16-bg font-thv16-sans px-6 py-14">
<div class="w-[min(760px,100%)] grid grid-cols-[repeat(auto-fit,minmax(260px,1fr))] gap-8">
<button type="button" class="relative overflow-hidden min-h-[220px] text-left rounded-2xl bg-white/[0.04] border border-white/10 cursor-pointer transition-[border-color,transform] duration-400 hover:border-thv16-teal/30 hover:-translate-y-1 focus-visible:border-thv16-teal/30 focus-visible:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-thv16-teal before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:opacity-0 before:transition-opacity before:duration-400 before:[background:radial-gradient(circle_at_50%_50%,rgba(20,184,166,.18)_0%,transparent_65%)] hover:before:opacity-100 focus-visible:before:opacity-100 motion-reduce:transition-colors motion-reduce:hover:translate-y-0">
<span class="relative z-10 flex flex-col gap-3 p-8">
<span class="text-2xl text-thv16-teal">◎</span>
<span class="text-base font-bold text-thv16-text">Center Spot</span>
<span class="text-sm leading-relaxed text-thv16-dim">Radial gradient fades in from the centre on hover.</span>
</span>
</button>
<button type="button" class="relative overflow-hidden min-h-[220px] text-left rounded-2xl bg-white/[0.04] border border-white/10 cursor-pointer transition-[border-color,transform] duration-400 hover:border-thv16-cyan/30 hover:-translate-y-1 focus-visible:border-thv16-cyan/30 focus-visible:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-thv16-cyan before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:opacity-0 before:transition-opacity before:duration-400 before:[background:conic-gradient(from_225deg_at_0%_0%,rgba(6,182,212,.25),transparent_40%)] hover:before:opacity-100 focus-visible:before:opacity-100 motion-reduce:transition-colors motion-reduce:hover:translate-y-0">
<span class="relative z-10 flex flex-col gap-3 p-8">
<span class="text-2xl text-thv16-cyan">◤</span>
<span class="text-base font-bold text-thv16-text">Corner Light</span>
<span class="text-sm leading-relaxed text-thv16-dim">Conic sweep from a lamp at the top-left corner.</span>
</span>
</button>
<button type="button" class="relative overflow-hidden min-h-[220px] text-left rounded-2xl bg-white/[0.04] border border-white/10 cursor-pointer transition-[border-color,transform] duration-400 hover:border-thv16-teal/30 hover:-translate-y-1 focus-visible:border-thv16-teal/30 focus-visible:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-thv16-teal before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:opacity-0 before:-translate-y-full before:transition-[opacity,transform] before:duration-500 before:ease-[cubic-bezier(.4,0,.2,1)] before:[background:linear-gradient(180deg,transparent_0%,rgba(20,184,166,.15)_40%,rgba(20,184,166,.15)_60%,transparent_100%)] hover:before:opacity-100 hover:before:translate-y-[60%] focus-visible:before:opacity-100 focus-visible:before:translate-y-[60%] motion-reduce:before:transition-opacity motion-reduce:hover:before:translate-y-0">
<span class="relative z-10 flex flex-col gap-3 p-8">
<span class="text-2xl text-thv16-teal">▤</span>
<span class="text-base font-bold text-thv16-text">Edge Scan</span>
<span class="text-sm leading-relaxed text-thv16-dim">A bright band sweeps top to bottom on hover.</span>
</span>
</button>
<button type="button" class="relative overflow-hidden min-h-[220px] text-left rounded-2xl bg-white/[0.04] border border-white/10 cursor-pointer transition-[border-color,transform] duration-400 hover:border-thv16-green/30 hover:-translate-y-1 focus-visible:border-thv16-green/30 focus-visible:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-thv16-green before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:opacity-100 before:transition-opacity before:duration-400 before:[background:radial-gradient(ellipse_at_center,transparent_40%,rgba(0,0,0,.7)_100%)] hover:before:opacity-0 focus-visible:before:opacity-0 motion-reduce:transition-colors motion-reduce:hover:translate-y-0">
<span class="relative z-10 flex flex-col gap-3 p-8">
<span class="text-2xl text-thv16-green">◉</span>
<span class="text-base font-bold text-thv16-text">Vignette Lift</span>
<span class="text-sm leading-relaxed text-thv16-dim">The dark vignette fades out, revealing the full card face.</span>
</span>
</button>
</div>
</section><section class="thv-16 w-full min-h-svh grid place-items-center bg-thv16-bg font-thv16-sans px-6 py-14">
<div class="w-[min(760px,100%)] grid grid-cols-[repeat(auto-fit,minmax(260px,1fr))] gap-8">
<button type="button" class="relative overflow-hidden min-h-[220px] text-left rounded-2xl bg-white/[0.04] border border-white/10 cursor-pointer transition-[border-color,transform] duration-400 hover:border-thv16-teal/30 hover:-translate-y-1 focus-visible:border-thv16-teal/30 focus-visible:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-thv16-teal before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:opacity-0 before:transition-opacity before:duration-400 before:[background:radial-gradient(circle_at_50%_50%,rgba(20,184,166,.18)_0%,transparent_65%)] hover:before:opacity-100 focus-visible:before:opacity-100 motion-reduce:transition-colors motion-reduce:hover:translate-y-0">
<span class="relative z-10 flex flex-col gap-3 p-8">
<span class="text-2xl text-thv16-teal">◎</span>
<span class="text-base font-bold text-thv16-text">Center Spot</span>
<span class="text-sm leading-relaxed text-thv16-dim">Radial gradient fades in from the centre on hover.</span>
</span>
</button>
<button type="button" class="relative overflow-hidden min-h-[220px] text-left rounded-2xl bg-white/[0.04] border border-white/10 cursor-pointer transition-[border-color,transform] duration-400 hover:border-thv16-cyan/30 hover:-translate-y-1 focus-visible:border-thv16-cyan/30 focus-visible:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-thv16-cyan before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:opacity-0 before:transition-opacity before:duration-400 before:[background:conic-gradient(from_225deg_at_0%_0%,rgba(6,182,212,.25),transparent_40%)] hover:before:opacity-100 focus-visible:before:opacity-100 motion-reduce:transition-colors motion-reduce:hover:translate-y-0">
<span class="relative z-10 flex flex-col gap-3 p-8">
<span class="text-2xl text-thv16-cyan">◤</span>
<span class="text-base font-bold text-thv16-text">Corner Light</span>
<span class="text-sm leading-relaxed text-thv16-dim">Conic sweep from a lamp at the top-left corner.</span>
</span>
</button>
<button type="button" class="relative overflow-hidden min-h-[220px] text-left rounded-2xl bg-white/[0.04] border border-white/10 cursor-pointer transition-[border-color,transform] duration-400 hover:border-thv16-teal/30 hover:-translate-y-1 focus-visible:border-thv16-teal/30 focus-visible:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-thv16-teal before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:opacity-0 before:-translate-y-full before:transition-[opacity,transform] before:duration-500 before:ease-[cubic-bezier(.4,0,.2,1)] before:[background:linear-gradient(180deg,transparent_0%,rgba(20,184,166,.15)_40%,rgba(20,184,166,.15)_60%,transparent_100%)] hover:before:opacity-100 hover:before:translate-y-[60%] focus-visible:before:opacity-100 focus-visible:before:translate-y-[60%] motion-reduce:before:transition-opacity motion-reduce:hover:before:translate-y-0">
<span class="relative z-10 flex flex-col gap-3 p-8">
<span class="text-2xl text-thv16-teal">▤</span>
<span class="text-base font-bold text-thv16-text">Edge Scan</span>
<span class="text-sm leading-relaxed text-thv16-dim">A bright band sweeps top to bottom on hover.</span>
</span>
</button>
<button type="button" class="relative overflow-hidden min-h-[220px] text-left rounded-2xl bg-white/[0.04] border border-white/10 cursor-pointer transition-[border-color,transform] duration-400 hover:border-thv16-green/30 hover:-translate-y-1 focus-visible:border-thv16-green/30 focus-visible:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-thv16-green before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:opacity-100 before:transition-opacity before:duration-400 before:[background:radial-gradient(ellipse_at_center,transparent_40%,rgba(0,0,0,.7)_100%)] hover:before:opacity-0 focus-visible:before:opacity-0 motion-reduce:transition-colors motion-reduce:hover:translate-y-0">
<span class="relative z-10 flex flex-col gap-3 p-8">
<span class="text-2xl text-thv16-green">◉</span>
<span class="text-base font-bold text-thv16-text">Vignette Lift</span>
<span class="text-sm leading-relaxed text-thv16-dim">The dark vignette fades out, revealing the full card face.</span>
</span>
</button>
</div>
</section>@import "tailwindcss";
@theme {
--color-thv16-bg: #020c0b;
--color-thv16-bg: oklch(0.13 0.02 180);
--color-thv16-text: #ccfbf1;
--color-thv16-text: oklch(0.94 0.05 175);
--color-thv16-dim: #94a3a0;
--color-thv16-dim: oklch(0.71 0.01 175);
--color-thv16-teal: #14b8a6;
--color-thv16-teal: oklch(0.72 0.13 180);
--color-thv16-green: #10b981;
--color-thv16-green: oklch(0.7 0.15 163);
--color-thv16-cyan: #06b6d4;
--color-thv16-cyan: oklch(0.71 0.14 208);
--font-thv16-sans: 'Space Grotesk', system-ui, sans-serif;
}
.thv-16 {
width: 100%;
min-height: 100svh;
}
.thv-16 ::selection {
background: var(--color-thv16-teal);
color: #fff;
}@import "tailwindcss";
@theme {
--color-thv16-bg: #020c0b;
--color-thv16-bg: oklch(0.13 0.02 180);
--color-thv16-text: #ccfbf1;
--color-thv16-text: oklch(0.94 0.05 175);
--color-thv16-dim: #94a3a0;
--color-thv16-dim: oklch(0.71 0.01 175);
--color-thv16-teal: #14b8a6;
--color-thv16-teal: oklch(0.72 0.13 180);
--color-thv16-green: #10b981;
--color-thv16-green: oklch(0.7 0.15 163);
--color-thv16-cyan: #06b6d4;
--color-thv16-cyan: oklch(0.71 0.14 208);
--font-thv16-sans: 'Space Grotesk', system-ui, sans-serif;
}
.thv-16 {
width: 100%;
min-height: 100svh;
}
.thv-16 ::selection {
background: var(--color-thv16-teal);
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-thv16-bg: #020c0b;
--color-thv16-bg: oklch(0.13 0.02 180);
--color-thv16-text: #ccfbf1;
--color-thv16-text: oklch(0.94 0.05 175);
--color-thv16-dim: #94a3a0;
--color-thv16-dim: oklch(0.71 0.01 175);
--color-thv16-teal: #14b8a6;
--color-thv16-teal: oklch(0.72 0.13 180);
--color-thv16-green: #10b981;
--color-thv16-green: oklch(0.7 0.15 163);
--color-thv16-cyan: #06b6d4;
--color-thv16-cyan: oklch(0.71 0.14 208);
--font-thv16-sans: 'Space Grotesk', system-ui, sans-serif;
}
.thv-16 {
width: 100%;
min-height: 100svh;
}
.thv-16 ::selection {
background: var(--color-thv16-teal);
color: #fff;
}@import "tailwindcss";
@theme {
--color-thv16-bg: #020c0b;
--color-thv16-bg: oklch(0.13 0.02 180);
--color-thv16-text: #ccfbf1;
--color-thv16-text: oklch(0.94 0.05 175);
--color-thv16-dim: #94a3a0;
--color-thv16-dim: oklch(0.71 0.01 175);
--color-thv16-teal: #14b8a6;
--color-thv16-teal: oklch(0.72 0.13 180);
--color-thv16-green: #10b981;
--color-thv16-green: oklch(0.7 0.15 163);
--color-thv16-cyan: #06b6d4;
--color-thv16-cyan: oklch(0.71 0.14 208);
--font-thv16-sans: 'Space Grotesk', system-ui, sans-serif;
}
.thv-16 {
width: 100%;
min-height: 100svh;
}
.thv-16 ::selection {
background: var(--color-thv16-teal);
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.
- Four lighting rigs, one skeleton: an overlay pseudo-element and one animated property each. The gradient is the only thing that differs.
- UI change from the CSS original: the body copy was lifted to a ~0.71-lightness token so it passes 4.5:1 under the vignette as well as over it, and each card is a focusable button that lights on
focus-visible:. - Under
motion-reduce:the lift is cancelled but the light still comes up — the state remains readable without translation.
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 Spotlight Card Hover Effect
Source: https://codefronts.com/motion/tailwind-css-hover-effects/tailwind-spotlight-card-hover-effect/
Four lighting treatments — centre spotlight, corner lamp, a travelling scan band and a vignette that lifts. Each is one gradient on a before: layer whose opacity (or position) changes on hover.
## HTML
```html
<section class="thv-16 w-full min-h-svh grid place-items-center bg-thv16-bg font-thv16-sans px-6 py-14">
<div class="w-[min(760px,100%)] grid grid-cols-[repeat(auto-fit,minmax(260px,1fr))] gap-8">
<button type="button" class="relative overflow-hidden min-h-[220px] text-left rounded-2xl bg-white/[0.04] border border-white/10 cursor-pointer transition-[border-color,transform] duration-400 hover:border-thv16-teal/30 hover:-translate-y-1 focus-visible:border-thv16-teal/30 focus-visible:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-thv16-teal before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:opacity-0 before:transition-opacity before:duration-400 before:[background:radial-gradient(circle_at_50%_50%,rgba(20,184,166,.18)_0%,transparent_65%)] hover:before:opacity-100 focus-visible:before:opacity-100 motion-reduce:transition-colors motion-reduce:hover:translate-y-0">
<span class="relative z-10 flex flex-col gap-3 p-8">
<span class="text-2xl text-thv16-teal">◎</span>
<span class="text-base font-bold text-thv16-text">Center Spot</span>
<span class="text-sm leading-relaxed text-thv16-dim">Radial gradient fades in from the centre on hover.</span>
</span>
</button>
<button type="button" class="relative overflow-hidden min-h-[220px] text-left rounded-2xl bg-white/[0.04] border border-white/10 cursor-pointer transition-[border-color,transform] duration-400 hover:border-thv16-cyan/30 hover:-translate-y-1 focus-visible:border-thv16-cyan/30 focus-visible:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-thv16-cyan before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:opacity-0 before:transition-opacity before:duration-400 before:[background:conic-gradient(from_225deg_at_0%_0%,rgba(6,182,212,.25),transparent_40%)] hover:before:opacity-100 focus-visible:before:opacity-100 motion-reduce:transition-colors motion-reduce:hover:translate-y-0">
<span class="relative z-10 flex flex-col gap-3 p-8">
<span class="text-2xl text-thv16-cyan">◤</span>
<span class="text-base font-bold text-thv16-text">Corner Light</span>
<span class="text-sm leading-relaxed text-thv16-dim">Conic sweep from a lamp at the top-left corner.</span>
</span>
</button>
<button type="button" class="relative overflow-hidden min-h-[220px] text-left rounded-2xl bg-white/[0.04] border border-white/10 cursor-pointer transition-[border-color,transform] duration-400 hover:border-thv16-teal/30 hover:-translate-y-1 focus-visible:border-thv16-teal/30 focus-visible:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-thv16-teal before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:opacity-0 before:-translate-y-full before:transition-[opacity,transform] before:duration-500 before:ease-[cubic-bezier(.4,0,.2,1)] before:[background:linear-gradient(180deg,transparent_0%,rgba(20,184,166,.15)_40%,rgba(20,184,166,.15)_60%,transparent_100%)] hover:before:opacity-100 hover:before:translate-y-[60%] focus-visible:before:opacity-100 focus-visible:before:translate-y-[60%] motion-reduce:before:transition-opacity motion-reduce:hover:before:translate-y-0">
<span class="relative z-10 flex flex-col gap-3 p-8">
<span class="text-2xl text-thv16-teal">▤</span>
<span class="text-base font-bold text-thv16-text">Edge Scan</span>
<span class="text-sm leading-relaxed text-thv16-dim">A bright band sweeps top to bottom on hover.</span>
</span>
</button>
<button type="button" class="relative overflow-hidden min-h-[220px] text-left rounded-2xl bg-white/[0.04] border border-white/10 cursor-pointer transition-[border-color,transform] duration-400 hover:border-thv16-green/30 hover:-translate-y-1 focus-visible:border-thv16-green/30 focus-visible:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-thv16-green before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:opacity-100 before:transition-opacity before:duration-400 before:[background:radial-gradient(ellipse_at_center,transparent_40%,rgba(0,0,0,.7)_100%)] hover:before:opacity-0 focus-visible:before:opacity-0 motion-reduce:transition-colors motion-reduce:hover:translate-y-0">
<span class="relative z-10 flex flex-col gap-3 p-8">
<span class="text-2xl text-thv16-green">◉</span>
<span class="text-base font-bold text-thv16-text">Vignette Lift</span>
<span class="text-sm leading-relaxed text-thv16-dim">The dark vignette fades out, revealing the full card face.</span>
</span>
</button>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-thv16-bg: #020c0b;
--color-thv16-bg: oklch(0.13 0.02 180);
--color-thv16-text: #ccfbf1;
--color-thv16-text: oklch(0.94 0.05 175);
--color-thv16-dim: #94a3a0;
--color-thv16-dim: oklch(0.71 0.01 175);
--color-thv16-teal: #14b8a6;
--color-thv16-teal: oklch(0.72 0.13 180);
--color-thv16-green: #10b981;
--color-thv16-green: oklch(0.7 0.15 163);
--color-thv16-cyan: #06b6d4;
--color-thv16-cyan: oklch(0.71 0.14 208);
--font-thv16-sans: 'Space Grotesk', system-ui, sans-serif;
}
.thv-16 {
width: 100%;
min-height: 100svh;
}
.thv-16 ::selection {
background: var(--color-thv16-teal);
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 Spotlight Card Hover Effect
Source: https://codefronts.com/motion/tailwind-css-hover-effects/tailwind-spotlight-card-hover-effect/
Four lighting treatments — centre spotlight, corner lamp, a travelling scan band and a vignette that lifts. Each is one gradient on a before: layer whose opacity (or position) changes on hover.
## HTML
```html
<section class="thv-16 w-full min-h-svh grid place-items-center bg-thv16-bg font-thv16-sans px-6 py-14">
<div class="w-[min(760px,100%)] grid grid-cols-[repeat(auto-fit,minmax(260px,1fr))] gap-8">
<button type="button" class="relative overflow-hidden min-h-[220px] text-left rounded-2xl bg-white/[0.04] border border-white/10 cursor-pointer transition-[border-color,transform] duration-400 hover:border-thv16-teal/30 hover:-translate-y-1 focus-visible:border-thv16-teal/30 focus-visible:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-thv16-teal before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:opacity-0 before:transition-opacity before:duration-400 before:[background:radial-gradient(circle_at_50%_50%,rgba(20,184,166,.18)_0%,transparent_65%)] hover:before:opacity-100 focus-visible:before:opacity-100 motion-reduce:transition-colors motion-reduce:hover:translate-y-0">
<span class="relative z-10 flex flex-col gap-3 p-8">
<span class="text-2xl text-thv16-teal">◎</span>
<span class="text-base font-bold text-thv16-text">Center Spot</span>
<span class="text-sm leading-relaxed text-thv16-dim">Radial gradient fades in from the centre on hover.</span>
</span>
</button>
<button type="button" class="relative overflow-hidden min-h-[220px] text-left rounded-2xl bg-white/[0.04] border border-white/10 cursor-pointer transition-[border-color,transform] duration-400 hover:border-thv16-cyan/30 hover:-translate-y-1 focus-visible:border-thv16-cyan/30 focus-visible:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-thv16-cyan before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:opacity-0 before:transition-opacity before:duration-400 before:[background:conic-gradient(from_225deg_at_0%_0%,rgba(6,182,212,.25),transparent_40%)] hover:before:opacity-100 focus-visible:before:opacity-100 motion-reduce:transition-colors motion-reduce:hover:translate-y-0">
<span class="relative z-10 flex flex-col gap-3 p-8">
<span class="text-2xl text-thv16-cyan">◤</span>
<span class="text-base font-bold text-thv16-text">Corner Light</span>
<span class="text-sm leading-relaxed text-thv16-dim">Conic sweep from a lamp at the top-left corner.</span>
</span>
</button>
<button type="button" class="relative overflow-hidden min-h-[220px] text-left rounded-2xl bg-white/[0.04] border border-white/10 cursor-pointer transition-[border-color,transform] duration-400 hover:border-thv16-teal/30 hover:-translate-y-1 focus-visible:border-thv16-teal/30 focus-visible:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-thv16-teal before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:opacity-0 before:-translate-y-full before:transition-[opacity,transform] before:duration-500 before:ease-[cubic-bezier(.4,0,.2,1)] before:[background:linear-gradient(180deg,transparent_0%,rgba(20,184,166,.15)_40%,rgba(20,184,166,.15)_60%,transparent_100%)] hover:before:opacity-100 hover:before:translate-y-[60%] focus-visible:before:opacity-100 focus-visible:before:translate-y-[60%] motion-reduce:before:transition-opacity motion-reduce:hover:before:translate-y-0">
<span class="relative z-10 flex flex-col gap-3 p-8">
<span class="text-2xl text-thv16-teal">▤</span>
<span class="text-base font-bold text-thv16-text">Edge Scan</span>
<span class="text-sm leading-relaxed text-thv16-dim">A bright band sweeps top to bottom on hover.</span>
</span>
</button>
<button type="button" class="relative overflow-hidden min-h-[220px] text-left rounded-2xl bg-white/[0.04] border border-white/10 cursor-pointer transition-[border-color,transform] duration-400 hover:border-thv16-green/30 hover:-translate-y-1 focus-visible:border-thv16-green/30 focus-visible:-translate-y-1 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-thv16-green before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:opacity-100 before:transition-opacity before:duration-400 before:[background:radial-gradient(ellipse_at_center,transparent_40%,rgba(0,0,0,.7)_100%)] hover:before:opacity-0 focus-visible:before:opacity-0 motion-reduce:transition-colors motion-reduce:hover:translate-y-0">
<span class="relative z-10 flex flex-col gap-3 p-8">
<span class="text-2xl text-thv16-green">◉</span>
<span class="text-base font-bold text-thv16-text">Vignette Lift</span>
<span class="text-sm leading-relaxed text-thv16-dim">The dark vignette fades out, revealing the full card face.</span>
</span>
</button>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-thv16-bg: #020c0b;
--color-thv16-bg: oklch(0.13 0.02 180);
--color-thv16-text: #ccfbf1;
--color-thv16-text: oklch(0.94 0.05 175);
--color-thv16-dim: #94a3a0;
--color-thv16-dim: oklch(0.71 0.01 175);
--color-thv16-teal: #14b8a6;
--color-thv16-teal: oklch(0.72 0.13 180);
--color-thv16-green: #10b981;
--color-thv16-green: oklch(0.7 0.15 163);
--color-thv16-cyan: #06b6d4;
--color-thv16-cyan: oklch(0.71 0.14 208);
--font-thv16-sans: 'Space Grotesk', system-ui, sans-serif;
}
.thv-16 {
width: 100%;
min-height: 100svh;
}
.thv-16 ::selection {
background: var(--color-thv16-teal);
color: #fff;
}
```How this works
Light is an overlay, and overlays are pseudo-elements. Every variant puts one gradient at before:absolute before:inset-0 before:pointer-events-none and animates a single property. Content sits at relative z-10 above it.
The scan band animates two properties. before:opacity-0 before:-translate-y-full at rest; on hover opacity-100 and translate-y-[60%], with different durations so the band fades in fast and travels slowly.
The vignette is the inverse case. Its overlay is fully opaque at rest and fades out on hover, which reads as the card being illuminated rather than a light being added.
Make it yours
- Move the light:
circle_at_80%_20%for a top-right source. - Warm it: swap the accent rgba for
rgba(255,200,100,.12). - Floodlight instead of spot: push the transparent stop from
65%to85%.
Gotchas — read before shipping
- The overlay must carry
pointer-events-noneor it eats clicks meant for content. mix-blend-*spotlights need an opaque parent; these use plain alpha to stay predictable on any ground.- Keep highlight alpha under ~0.2 — a brighter spot washes out body copy and fails contrast.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Radial and conic gradients are Baseline; the floor is Tailwind v4's generated sheet.