20 / 30
All 30 in 30 Tailwind CSS Text Hover Effects
- 01Tailwind CSS Animated Underline Hover Effect
- 02Tailwind Liquid Text Fill Hover Animation
- 03Tailwind Gradient Text Hover Effect with bg-clip-text
- 04Tailwind Animated Strikethrough Text Hover Effect
- 05Tailwind Text Swap and Flip Up Hover Effect
- 06Tailwind CSS Glitch Text Hover Effect
- 07Tailwind Neon Glow Text with Flicker Animation
- 08Tailwind Metallic Gold Shimmer Text Effect
- 09Tailwind 3D Text Pop and Lift Hover Effect
- 10Tailwind Staggered Letter Wave Hover Animation
- 11Tailwind Spotlight Text Reveal on Mouse Hover
- 12Tailwind Hover to Reveal Hidden Spoiler Text
- 13Tailwind Blur to Focus Text Hover Effect
- 14Tailwind Magnetic Hover Effect for Buttons and Text
- 15Tailwind Letter Spacing Expand on Hover (Tracking Animation)
- 16Tailwind Outline Text Stroke to Fill Hover Effect
- 17Tailwind Text Scramble and Matrix Decrypt Hover Effect
- 18Tailwind Animated Border Button Hover Effect
- 19Tailwind Elastic Rubber Band Text Hover Animation
- 20Tailwind Variable Font Weight Hover Animation
- 21Tailwind Animated Rainbow Gradient Text on Hover
- 22Tailwind 3D Perspective Tilt Text Hover Effect
- 23Tailwind Smoke Dissolve Text Hover Effect
- 24Tailwind Fire Text Effect with Heat Wave Glow
- 25Tailwind Dynamic Text Shadow that Follows the Cursor
- 26Tailwind Infinite Text Marquee that Speeds Up on Hover
- 27Tailwind Duotone Split Color Text Hover Effect
- 28Tailwind CSS Tooltip on Hover (No JavaScript)
- 29Tailwind SVG Water Ripple Distortion Text Effect
- 30Tailwind Clip-Path Text Reveal Hover Animation
Tailwind Variable Font Weight Hover Animation
An issue index where titles glide from font-light to font-extrabold through every intermediate weight — with zero layout shift. A hidden bold twin, after:content-[attr(data-text)] after:h-0 after:invisible after:font-extrabold, reserves the bold width from the first paint.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tth-20 w-full min-h-svh grid place-items-center bg-tth20-bg px-5 py-14 font-tth20-sans text-tth20-ink [font-synthesis-weight:none]" aria-label="Variable font weight shift demo">
<div class="flex w-full max-w-[45rem] flex-col gap-[clamp(16px,3vw,24px)]">
<header class="flex flex-wrap items-center justify-between gap-3.5 border-b-[1.5px] border-tth20-ink pb-3.5">
<p class="text-[12.5px] font-bold uppercase tracking-[0.2em] text-tth20-mut">Grain & Gradient — issue index</p>
<span class="rounded-full border border-tth20-acc/40 px-3 py-1.5 font-tth20-mono text-[11px] text-tth20-acc">wght 300 → 800, one axis</span>
</header>
<nav class="flex flex-col" aria-label="Issues">
<a id="tth20-top" href="#tth20-top" class="group/item flex items-baseline gap-[clamp(14px,3vw,24px)] rounded border-b border-tth20-line px-1 py-[clamp(12px,2.4vw,18px)] no-underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tth20-acc"><em class="font-tth20-mono text-xs font-bold not-italic text-tth20-mut transition-[font-weight,color] duration-450 group-hover/item:font-normal group-hover/item:text-tth20-mut/60 group-focus-visible/item:text-tth20-mut/60">01</em><span data-text="The Typography Issue" class="block text-[clamp(24px,4.8vw,40px)] font-light leading-[1.2] tracking-[-0.015em] transition-[font-weight,color] duration-450 after:invisible after:block after:h-0 after:overflow-hidden after:font-extrabold after:content-[attr(data-text)] group-hover/item:font-extrabold group-hover/item:text-tth20-acc group-focus-visible/item:font-extrabold group-focus-visible/item:text-tth20-acc motion-reduce:transition-none">The Typography Issue</span><span aria-hidden="true" class="ml-auto -translate-x-2 text-xl text-tth20-acc opacity-0 transition-[opacity,translate] duration-300 group-hover/item:translate-x-0 group-hover/item:opacity-100 group-focus-visible/item:opacity-100">→</span></a>
<a href="#tth20-top" class="group/item flex items-baseline gap-[clamp(14px,3vw,24px)] rounded border-b border-tth20-line px-1 py-[clamp(12px,2.4vw,18px)] no-underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tth20-acc"><em class="font-tth20-mono text-xs font-bold not-italic text-tth20-mut transition-[font-weight,color] duration-450 group-hover/item:font-normal group-hover/item:text-tth20-mut/60 group-focus-visible/item:text-tth20-mut/60">02</em><span data-text="Color, Carefully" class="block text-[clamp(24px,4.8vw,40px)] font-light leading-[1.2] tracking-[-0.015em] transition-[font-weight,color] duration-450 after:invisible after:block after:h-0 after:overflow-hidden after:font-extrabold after:content-[attr(data-text)] group-hover/item:font-extrabold group-hover/item:text-tth20-acc group-focus-visible/item:font-extrabold group-focus-visible/item:text-tth20-acc motion-reduce:transition-none">Color, Carefully</span><span aria-hidden="true" class="ml-auto -translate-x-2 text-xl text-tth20-acc opacity-0 transition-[opacity,translate] duration-300 group-hover/item:translate-x-0 group-hover/item:opacity-100 group-focus-visible/item:opacity-100">→</span></a>
<a href="#tth20-top" class="group/item flex items-baseline gap-[clamp(14px,3vw,24px)] rounded border-b border-tth20-line px-1 py-[clamp(12px,2.4vw,18px)] no-underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tth20-acc"><em class="font-tth20-mono text-xs font-bold not-italic text-tth20-mut transition-[font-weight,color] duration-450 group-hover/item:font-normal group-hover/item:text-tth20-mut/60 group-focus-visible/item:text-tth20-mut/60">03</em><span data-text="Grids Against Chaos" class="block text-[clamp(24px,4.8vw,40px)] font-light leading-[1.2] tracking-[-0.015em] transition-[font-weight,color] duration-450 after:invisible after:block after:h-0 after:overflow-hidden after:font-extrabold after:content-[attr(data-text)] group-hover/item:font-extrabold group-hover/item:text-tth20-acc group-focus-visible/item:font-extrabold group-focus-visible/item:text-tth20-acc motion-reduce:transition-none">Grids Against Chaos</span><span aria-hidden="true" class="ml-auto -translate-x-2 text-xl text-tth20-acc opacity-0 transition-[opacity,translate] duration-300 group-hover/item:translate-x-0 group-hover/item:opacity-100 group-focus-visible/item:opacity-100">→</span></a>
<a href="#tth20-top" class="group/item flex items-baseline gap-[clamp(14px,3vw,24px)] rounded border-b border-tth20-line px-1 py-[clamp(12px,2.4vw,18px)] no-underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tth20-acc"><em class="font-tth20-mono text-xs font-bold not-italic text-tth20-mut transition-[font-weight,color] duration-450 group-hover/item:font-normal group-hover/item:text-tth20-mut/60 group-focus-visible/item:text-tth20-mut/60">04</em><span data-text="Motion With Manners" class="block text-[clamp(24px,4.8vw,40px)] font-light leading-[1.2] tracking-[-0.015em] transition-[font-weight,color] duration-450 after:invisible after:block after:h-0 after:overflow-hidden after:font-extrabold after:content-[attr(data-text)] group-hover/item:font-extrabold group-hover/item:text-tth20-acc group-focus-visible/item:font-extrabold group-focus-visible/item:text-tth20-acc motion-reduce:transition-none">Motion With Manners</span><span aria-hidden="true" class="ml-auto -translate-x-2 text-xl text-tth20-acc opacity-0 transition-[opacity,translate] duration-300 group-hover/item:translate-x-0 group-hover/item:opacity-100 group-focus-visible/item:opacity-100">→</span></a>
</nav>
<p class="max-w-[60ch] text-[12.5px] leading-[1.7] text-tth20-mut">Hover a title: every intermediate weight renders — and the row never shifts, because a hidden bold twin (<code class="rounded bg-tth20-chip px-1.5 py-px font-tth20-mono text-[0.9em]">after:content-[attr(data-text)]</code>) reserved the width on day one.</p>
</div>
</section><section class="tth-20 w-full min-h-svh grid place-items-center bg-tth20-bg px-5 py-14 font-tth20-sans text-tth20-ink [font-synthesis-weight:none]" aria-label="Variable font weight shift demo">
<div class="flex w-full max-w-[45rem] flex-col gap-[clamp(16px,3vw,24px)]">
<header class="flex flex-wrap items-center justify-between gap-3.5 border-b-[1.5px] border-tth20-ink pb-3.5">
<p class="text-[12.5px] font-bold uppercase tracking-[0.2em] text-tth20-mut">Grain & Gradient — issue index</p>
<span class="rounded-full border border-tth20-acc/40 px-3 py-1.5 font-tth20-mono text-[11px] text-tth20-acc">wght 300 → 800, one axis</span>
</header>
<nav class="flex flex-col" aria-label="Issues">
<a id="tth20-top" href="#tth20-top" class="group/item flex items-baseline gap-[clamp(14px,3vw,24px)] rounded border-b border-tth20-line px-1 py-[clamp(12px,2.4vw,18px)] no-underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tth20-acc"><em class="font-tth20-mono text-xs font-bold not-italic text-tth20-mut transition-[font-weight,color] duration-450 group-hover/item:font-normal group-hover/item:text-tth20-mut/60 group-focus-visible/item:text-tth20-mut/60">01</em><span data-text="The Typography Issue" class="block text-[clamp(24px,4.8vw,40px)] font-light leading-[1.2] tracking-[-0.015em] transition-[font-weight,color] duration-450 after:invisible after:block after:h-0 after:overflow-hidden after:font-extrabold after:content-[attr(data-text)] group-hover/item:font-extrabold group-hover/item:text-tth20-acc group-focus-visible/item:font-extrabold group-focus-visible/item:text-tth20-acc motion-reduce:transition-none">The Typography Issue</span><span aria-hidden="true" class="ml-auto -translate-x-2 text-xl text-tth20-acc opacity-0 transition-[opacity,translate] duration-300 group-hover/item:translate-x-0 group-hover/item:opacity-100 group-focus-visible/item:opacity-100">→</span></a>
<a href="#tth20-top" class="group/item flex items-baseline gap-[clamp(14px,3vw,24px)] rounded border-b border-tth20-line px-1 py-[clamp(12px,2.4vw,18px)] no-underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tth20-acc"><em class="font-tth20-mono text-xs font-bold not-italic text-tth20-mut transition-[font-weight,color] duration-450 group-hover/item:font-normal group-hover/item:text-tth20-mut/60 group-focus-visible/item:text-tth20-mut/60">02</em><span data-text="Color, Carefully" class="block text-[clamp(24px,4.8vw,40px)] font-light leading-[1.2] tracking-[-0.015em] transition-[font-weight,color] duration-450 after:invisible after:block after:h-0 after:overflow-hidden after:font-extrabold after:content-[attr(data-text)] group-hover/item:font-extrabold group-hover/item:text-tth20-acc group-focus-visible/item:font-extrabold group-focus-visible/item:text-tth20-acc motion-reduce:transition-none">Color, Carefully</span><span aria-hidden="true" class="ml-auto -translate-x-2 text-xl text-tth20-acc opacity-0 transition-[opacity,translate] duration-300 group-hover/item:translate-x-0 group-hover/item:opacity-100 group-focus-visible/item:opacity-100">→</span></a>
<a href="#tth20-top" class="group/item flex items-baseline gap-[clamp(14px,3vw,24px)] rounded border-b border-tth20-line px-1 py-[clamp(12px,2.4vw,18px)] no-underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tth20-acc"><em class="font-tth20-mono text-xs font-bold not-italic text-tth20-mut transition-[font-weight,color] duration-450 group-hover/item:font-normal group-hover/item:text-tth20-mut/60 group-focus-visible/item:text-tth20-mut/60">03</em><span data-text="Grids Against Chaos" class="block text-[clamp(24px,4.8vw,40px)] font-light leading-[1.2] tracking-[-0.015em] transition-[font-weight,color] duration-450 after:invisible after:block after:h-0 after:overflow-hidden after:font-extrabold after:content-[attr(data-text)] group-hover/item:font-extrabold group-hover/item:text-tth20-acc group-focus-visible/item:font-extrabold group-focus-visible/item:text-tth20-acc motion-reduce:transition-none">Grids Against Chaos</span><span aria-hidden="true" class="ml-auto -translate-x-2 text-xl text-tth20-acc opacity-0 transition-[opacity,translate] duration-300 group-hover/item:translate-x-0 group-hover/item:opacity-100 group-focus-visible/item:opacity-100">→</span></a>
<a href="#tth20-top" class="group/item flex items-baseline gap-[clamp(14px,3vw,24px)] rounded border-b border-tth20-line px-1 py-[clamp(12px,2.4vw,18px)] no-underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tth20-acc"><em class="font-tth20-mono text-xs font-bold not-italic text-tth20-mut transition-[font-weight,color] duration-450 group-hover/item:font-normal group-hover/item:text-tth20-mut/60 group-focus-visible/item:text-tth20-mut/60">04</em><span data-text="Motion With Manners" class="block text-[clamp(24px,4.8vw,40px)] font-light leading-[1.2] tracking-[-0.015em] transition-[font-weight,color] duration-450 after:invisible after:block after:h-0 after:overflow-hidden after:font-extrabold after:content-[attr(data-text)] group-hover/item:font-extrabold group-hover/item:text-tth20-acc group-focus-visible/item:font-extrabold group-focus-visible/item:text-tth20-acc motion-reduce:transition-none">Motion With Manners</span><span aria-hidden="true" class="ml-auto -translate-x-2 text-xl text-tth20-acc opacity-0 transition-[opacity,translate] duration-300 group-hover/item:translate-x-0 group-hover/item:opacity-100 group-focus-visible/item:opacity-100">→</span></a>
</nav>
<p class="max-w-[60ch] text-[12.5px] leading-[1.7] text-tth20-mut">Hover a title: every intermediate weight renders — and the row never shifts, because a hidden bold twin (<code class="rounded bg-tth20-chip px-1.5 py-px font-tth20-mono text-[0.9em]">after:content-[attr(data-text)]</code>) reserved the width on day one.</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-tth20-bg: #f9f6f3;
--color-tth20-bg: oklch(0.97 0.005 60);
--color-tth20-chip: #ece8e3;
--color-tth20-chip: oklch(0.92 0.006 55);
--color-tth20-ink: #221a14;
--color-tth20-ink: oklch(0.22 0.012 50);
--color-tth20-mut: #6b6158;
--color-tth20-mut: oklch(0.5 0.015 55);
--color-tth20-acc: #bf5427;
--color-tth20-acc: oklch(0.58 0.16 40);
--color-tth20-line: #e3ded8;
--color-tth20-line: oklch(0.89 0.008 55);
--font-tth20-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-tth20-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tth-20 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tth20-bg: #f9f6f3;
--color-tth20-bg: oklch(0.97 0.005 60);
--color-tth20-chip: #ece8e3;
--color-tth20-chip: oklch(0.92 0.006 55);
--color-tth20-ink: #221a14;
--color-tth20-ink: oklch(0.22 0.012 50);
--color-tth20-mut: #6b6158;
--color-tth20-mut: oklch(0.5 0.015 55);
--color-tth20-acc: #bf5427;
--color-tth20-acc: oklch(0.58 0.16 40);
--color-tth20-line: #e3ded8;
--color-tth20-line: oklch(0.89 0.008 55);
--font-tth20-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-tth20-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tth-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";
@theme {
--color-tth20-bg: #f9f6f3;
--color-tth20-bg: oklch(0.97 0.005 60);
--color-tth20-chip: #ece8e3;
--color-tth20-chip: oklch(0.92 0.006 55);
--color-tth20-ink: #221a14;
--color-tth20-ink: oklch(0.22 0.012 50);
--color-tth20-mut: #6b6158;
--color-tth20-mut: oklch(0.5 0.015 55);
--color-tth20-acc: #bf5427;
--color-tth20-acc: oklch(0.58 0.16 40);
--color-tth20-line: #e3ded8;
--color-tth20-line: oklch(0.89 0.008 55);
--font-tth20-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-tth20-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tth-20 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tth20-bg: #f9f6f3;
--color-tth20-bg: oklch(0.97 0.005 60);
--color-tth20-chip: #ece8e3;
--color-tth20-chip: oklch(0.92 0.006 55);
--color-tth20-ink: #221a14;
--color-tth20-ink: oklch(0.22 0.012 50);
--color-tth20-mut: #6b6158;
--color-tth20-mut: oklch(0.5 0.015 55);
--color-tth20-acc: #bf5427;
--color-tth20-acc: oklch(0.58 0.16 40);
--color-tth20-line: #e3ded8;
--color-tth20-line: oklch(0.89 0.008 55);
--font-tth20-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-tth20-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tth-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=Plus+Jakarta+Sans:wght@200..800&family=JetBrains+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=Plus+Jakarta+Sans:wght@200..800&family=JetBrains+Mono:wght@400;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The CSS original loaded its variable font with
@import url(…)inside the stylesheet; in Tailwind v4 user@imports are emitted after the layers and dropped, so the font now loads fromsetup. .label::after{content:attr(data-text);height:0;visibility:hidden;font-weight:800}became sevenafter:utilities on the label.- UI upgrade from the CSS original: a directional arrow fades and slides in on the active row, so the index reads as navigation rather than a list of headings.
- UI upgrade: muted numerals rose from
0.55to0.5L (5.5:1), and Plus Jakarta Sans gives a wider weight range with crisper light weights.
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 Variable Font Weight Hover Animation
Source: https://codefronts.com/motion/tailwind-css-text-hover-effects/tailwind-variable-font-weight-hover-animation/
An issue index where titles glide from font-light to font-extrabold through every intermediate weight — with zero layout shift. A hidden bold twin, after:content-[attr(data-text)] after:h-0 after:invisible after:font-extrabold, reserves the bold width from the first paint.
## HTML
```html
<section class="tth-20 w-full min-h-svh grid place-items-center bg-tth20-bg px-5 py-14 font-tth20-sans text-tth20-ink [font-synthesis-weight:none]" aria-label="Variable font weight shift demo">
<div class="flex w-full max-w-[45rem] flex-col gap-[clamp(16px,3vw,24px)]">
<header class="flex flex-wrap items-center justify-between gap-3.5 border-b-[1.5px] border-tth20-ink pb-3.5">
<p class="text-[12.5px] font-bold uppercase tracking-[0.2em] text-tth20-mut">Grain & Gradient — issue index</p>
<span class="rounded-full border border-tth20-acc/40 px-3 py-1.5 font-tth20-mono text-[11px] text-tth20-acc">wght 300 → 800, one axis</span>
</header>
<nav class="flex flex-col" aria-label="Issues">
<a id="tth20-top" href="#tth20-top" class="group/item flex items-baseline gap-[clamp(14px,3vw,24px)] rounded border-b border-tth20-line px-1 py-[clamp(12px,2.4vw,18px)] no-underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tth20-acc"><em class="font-tth20-mono text-xs font-bold not-italic text-tth20-mut transition-[font-weight,color] duration-450 group-hover/item:font-normal group-hover/item:text-tth20-mut/60 group-focus-visible/item:text-tth20-mut/60">01</em><span data-text="The Typography Issue" class="block text-[clamp(24px,4.8vw,40px)] font-light leading-[1.2] tracking-[-0.015em] transition-[font-weight,color] duration-450 after:invisible after:block after:h-0 after:overflow-hidden after:font-extrabold after:content-[attr(data-text)] group-hover/item:font-extrabold group-hover/item:text-tth20-acc group-focus-visible/item:font-extrabold group-focus-visible/item:text-tth20-acc motion-reduce:transition-none">The Typography Issue</span><span aria-hidden="true" class="ml-auto -translate-x-2 text-xl text-tth20-acc opacity-0 transition-[opacity,translate] duration-300 group-hover/item:translate-x-0 group-hover/item:opacity-100 group-focus-visible/item:opacity-100">→</span></a>
<a href="#tth20-top" class="group/item flex items-baseline gap-[clamp(14px,3vw,24px)] rounded border-b border-tth20-line px-1 py-[clamp(12px,2.4vw,18px)] no-underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tth20-acc"><em class="font-tth20-mono text-xs font-bold not-italic text-tth20-mut transition-[font-weight,color] duration-450 group-hover/item:font-normal group-hover/item:text-tth20-mut/60 group-focus-visible/item:text-tth20-mut/60">02</em><span data-text="Color, Carefully" class="block text-[clamp(24px,4.8vw,40px)] font-light leading-[1.2] tracking-[-0.015em] transition-[font-weight,color] duration-450 after:invisible after:block after:h-0 after:overflow-hidden after:font-extrabold after:content-[attr(data-text)] group-hover/item:font-extrabold group-hover/item:text-tth20-acc group-focus-visible/item:font-extrabold group-focus-visible/item:text-tth20-acc motion-reduce:transition-none">Color, Carefully</span><span aria-hidden="true" class="ml-auto -translate-x-2 text-xl text-tth20-acc opacity-0 transition-[opacity,translate] duration-300 group-hover/item:translate-x-0 group-hover/item:opacity-100 group-focus-visible/item:opacity-100">→</span></a>
<a href="#tth20-top" class="group/item flex items-baseline gap-[clamp(14px,3vw,24px)] rounded border-b border-tth20-line px-1 py-[clamp(12px,2.4vw,18px)] no-underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tth20-acc"><em class="font-tth20-mono text-xs font-bold not-italic text-tth20-mut transition-[font-weight,color] duration-450 group-hover/item:font-normal group-hover/item:text-tth20-mut/60 group-focus-visible/item:text-tth20-mut/60">03</em><span data-text="Grids Against Chaos" class="block text-[clamp(24px,4.8vw,40px)] font-light leading-[1.2] tracking-[-0.015em] transition-[font-weight,color] duration-450 after:invisible after:block after:h-0 after:overflow-hidden after:font-extrabold after:content-[attr(data-text)] group-hover/item:font-extrabold group-hover/item:text-tth20-acc group-focus-visible/item:font-extrabold group-focus-visible/item:text-tth20-acc motion-reduce:transition-none">Grids Against Chaos</span><span aria-hidden="true" class="ml-auto -translate-x-2 text-xl text-tth20-acc opacity-0 transition-[opacity,translate] duration-300 group-hover/item:translate-x-0 group-hover/item:opacity-100 group-focus-visible/item:opacity-100">→</span></a>
<a href="#tth20-top" class="group/item flex items-baseline gap-[clamp(14px,3vw,24px)] rounded border-b border-tth20-line px-1 py-[clamp(12px,2.4vw,18px)] no-underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tth20-acc"><em class="font-tth20-mono text-xs font-bold not-italic text-tth20-mut transition-[font-weight,color] duration-450 group-hover/item:font-normal group-hover/item:text-tth20-mut/60 group-focus-visible/item:text-tth20-mut/60">04</em><span data-text="Motion With Manners" class="block text-[clamp(24px,4.8vw,40px)] font-light leading-[1.2] tracking-[-0.015em] transition-[font-weight,color] duration-450 after:invisible after:block after:h-0 after:overflow-hidden after:font-extrabold after:content-[attr(data-text)] group-hover/item:font-extrabold group-hover/item:text-tth20-acc group-focus-visible/item:font-extrabold group-focus-visible/item:text-tth20-acc motion-reduce:transition-none">Motion With Manners</span><span aria-hidden="true" class="ml-auto -translate-x-2 text-xl text-tth20-acc opacity-0 transition-[opacity,translate] duration-300 group-hover/item:translate-x-0 group-hover/item:opacity-100 group-focus-visible/item:opacity-100">→</span></a>
</nav>
<p class="max-w-[60ch] text-[12.5px] leading-[1.7] text-tth20-mut">Hover a title: every intermediate weight renders — and the row never shifts, because a hidden bold twin (<code class="rounded bg-tth20-chip px-1.5 py-px font-tth20-mono text-[0.9em]">after:content-[attr(data-text)]</code>) reserved the width on day one.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tth20-bg: #f9f6f3;
--color-tth20-bg: oklch(0.97 0.005 60);
--color-tth20-chip: #ece8e3;
--color-tth20-chip: oklch(0.92 0.006 55);
--color-tth20-ink: #221a14;
--color-tth20-ink: oklch(0.22 0.012 50);
--color-tth20-mut: #6b6158;
--color-tth20-mut: oklch(0.5 0.015 55);
--color-tth20-acc: #bf5427;
--color-tth20-acc: oklch(0.58 0.16 40);
--color-tth20-line: #e3ded8;
--color-tth20-line: oklch(0.89 0.008 55);
--font-tth20-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-tth20-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tth-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 Variable Font Weight Hover Animation
Source: https://codefronts.com/motion/tailwind-css-text-hover-effects/tailwind-variable-font-weight-hover-animation/
An issue index where titles glide from font-light to font-extrabold through every intermediate weight — with zero layout shift. A hidden bold twin, after:content-[attr(data-text)] after:h-0 after:invisible after:font-extrabold, reserves the bold width from the first paint.
## HTML
```html
<section class="tth-20 w-full min-h-svh grid place-items-center bg-tth20-bg px-5 py-14 font-tth20-sans text-tth20-ink [font-synthesis-weight:none]" aria-label="Variable font weight shift demo">
<div class="flex w-full max-w-[45rem] flex-col gap-[clamp(16px,3vw,24px)]">
<header class="flex flex-wrap items-center justify-between gap-3.5 border-b-[1.5px] border-tth20-ink pb-3.5">
<p class="text-[12.5px] font-bold uppercase tracking-[0.2em] text-tth20-mut">Grain & Gradient — issue index</p>
<span class="rounded-full border border-tth20-acc/40 px-3 py-1.5 font-tth20-mono text-[11px] text-tth20-acc">wght 300 → 800, one axis</span>
</header>
<nav class="flex flex-col" aria-label="Issues">
<a id="tth20-top" href="#tth20-top" class="group/item flex items-baseline gap-[clamp(14px,3vw,24px)] rounded border-b border-tth20-line px-1 py-[clamp(12px,2.4vw,18px)] no-underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tth20-acc"><em class="font-tth20-mono text-xs font-bold not-italic text-tth20-mut transition-[font-weight,color] duration-450 group-hover/item:font-normal group-hover/item:text-tth20-mut/60 group-focus-visible/item:text-tth20-mut/60">01</em><span data-text="The Typography Issue" class="block text-[clamp(24px,4.8vw,40px)] font-light leading-[1.2] tracking-[-0.015em] transition-[font-weight,color] duration-450 after:invisible after:block after:h-0 after:overflow-hidden after:font-extrabold after:content-[attr(data-text)] group-hover/item:font-extrabold group-hover/item:text-tth20-acc group-focus-visible/item:font-extrabold group-focus-visible/item:text-tth20-acc motion-reduce:transition-none">The Typography Issue</span><span aria-hidden="true" class="ml-auto -translate-x-2 text-xl text-tth20-acc opacity-0 transition-[opacity,translate] duration-300 group-hover/item:translate-x-0 group-hover/item:opacity-100 group-focus-visible/item:opacity-100">→</span></a>
<a href="#tth20-top" class="group/item flex items-baseline gap-[clamp(14px,3vw,24px)] rounded border-b border-tth20-line px-1 py-[clamp(12px,2.4vw,18px)] no-underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tth20-acc"><em class="font-tth20-mono text-xs font-bold not-italic text-tth20-mut transition-[font-weight,color] duration-450 group-hover/item:font-normal group-hover/item:text-tth20-mut/60 group-focus-visible/item:text-tth20-mut/60">02</em><span data-text="Color, Carefully" class="block text-[clamp(24px,4.8vw,40px)] font-light leading-[1.2] tracking-[-0.015em] transition-[font-weight,color] duration-450 after:invisible after:block after:h-0 after:overflow-hidden after:font-extrabold after:content-[attr(data-text)] group-hover/item:font-extrabold group-hover/item:text-tth20-acc group-focus-visible/item:font-extrabold group-focus-visible/item:text-tth20-acc motion-reduce:transition-none">Color, Carefully</span><span aria-hidden="true" class="ml-auto -translate-x-2 text-xl text-tth20-acc opacity-0 transition-[opacity,translate] duration-300 group-hover/item:translate-x-0 group-hover/item:opacity-100 group-focus-visible/item:opacity-100">→</span></a>
<a href="#tth20-top" class="group/item flex items-baseline gap-[clamp(14px,3vw,24px)] rounded border-b border-tth20-line px-1 py-[clamp(12px,2.4vw,18px)] no-underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tth20-acc"><em class="font-tth20-mono text-xs font-bold not-italic text-tth20-mut transition-[font-weight,color] duration-450 group-hover/item:font-normal group-hover/item:text-tth20-mut/60 group-focus-visible/item:text-tth20-mut/60">03</em><span data-text="Grids Against Chaos" class="block text-[clamp(24px,4.8vw,40px)] font-light leading-[1.2] tracking-[-0.015em] transition-[font-weight,color] duration-450 after:invisible after:block after:h-0 after:overflow-hidden after:font-extrabold after:content-[attr(data-text)] group-hover/item:font-extrabold group-hover/item:text-tth20-acc group-focus-visible/item:font-extrabold group-focus-visible/item:text-tth20-acc motion-reduce:transition-none">Grids Against Chaos</span><span aria-hidden="true" class="ml-auto -translate-x-2 text-xl text-tth20-acc opacity-0 transition-[opacity,translate] duration-300 group-hover/item:translate-x-0 group-hover/item:opacity-100 group-focus-visible/item:opacity-100">→</span></a>
<a href="#tth20-top" class="group/item flex items-baseline gap-[clamp(14px,3vw,24px)] rounded border-b border-tth20-line px-1 py-[clamp(12px,2.4vw,18px)] no-underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tth20-acc"><em class="font-tth20-mono text-xs font-bold not-italic text-tth20-mut transition-[font-weight,color] duration-450 group-hover/item:font-normal group-hover/item:text-tth20-mut/60 group-focus-visible/item:text-tth20-mut/60">04</em><span data-text="Motion With Manners" class="block text-[clamp(24px,4.8vw,40px)] font-light leading-[1.2] tracking-[-0.015em] transition-[font-weight,color] duration-450 after:invisible after:block after:h-0 after:overflow-hidden after:font-extrabold after:content-[attr(data-text)] group-hover/item:font-extrabold group-hover/item:text-tth20-acc group-focus-visible/item:font-extrabold group-focus-visible/item:text-tth20-acc motion-reduce:transition-none">Motion With Manners</span><span aria-hidden="true" class="ml-auto -translate-x-2 text-xl text-tth20-acc opacity-0 transition-[opacity,translate] duration-300 group-hover/item:translate-x-0 group-hover/item:opacity-100 group-focus-visible/item:opacity-100">→</span></a>
</nav>
<p class="max-w-[60ch] text-[12.5px] leading-[1.7] text-tth20-mut">Hover a title: every intermediate weight renders — and the row never shifts, because a hidden bold twin (<code class="rounded bg-tth20-chip px-1.5 py-px font-tth20-mono text-[0.9em]">after:content-[attr(data-text)]</code>) reserved the width on day one.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tth20-bg: #f9f6f3;
--color-tth20-bg: oklch(0.97 0.005 60);
--color-tth20-chip: #ece8e3;
--color-tth20-chip: oklch(0.92 0.006 55);
--color-tth20-ink: #221a14;
--color-tth20-ink: oklch(0.22 0.012 50);
--color-tth20-mut: #6b6158;
--color-tth20-mut: oklch(0.5 0.015 55);
--color-tth20-acc: #bf5427;
--color-tth20-acc: oklch(0.58 0.16 40);
--color-tth20-line: #e3ded8;
--color-tth20-line: oklch(0.89 0.008 55);
--font-tth20-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-tth20-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tth-20 {
width: 100%;
min-height: 100svh;
}
```How this works
Weight is width. Bolder glyphs are wider, so a menu item that bolds on hover pushes its neighbours. The fix is a zero-height, invisible copy of the label rendered at the bold weight underneath it — the box is always as wide as the bold version:
<span data-text="Color, Carefully" class="block font-light
after:block after:h-0 after:overflow-hidden after:invisible
after:font-extrabold after:content-[attr(data-text)]
group-hover/item:font-extrabold">Real interpolation needs a variable font. With a 200–800 axis, transition-[font-weight] renders every weight in between; with static fonts it snaps.
The numeral does the opposite — it thins and fades — so the eye moves to the title.
Make it yours
- Subtler:
font-normal→font-semiboldfor dense nav. - Add width: fonts with a
wdthaxis can animate[font-stretch:…]too. - Italic slant: a
slntaxis via[font-variation-settings:'slnt'_-10].
Gotchas — read before shipping
- Without
data-textmatching the label, the reservation is the wrong width. - System fonts aren't reliably variable — always load one.
font-synthesis-weight:nonestops browsers inventing a fake bold if the font fails.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Variable fonts and font-weight transitions are universal.