30 Tailwind CSS text hover effects for navbar links, agency wordmarks, hero headlines, CTA buttons, and editorial indexes — spanning animated underlines, gradient and liquid fills, glitch and neon glow, metallic shimmer, 3D pop and perspective tilt, staggered letter waves, spotlight and clip-path reveals, and scramble decrypts. Scoped under .tth-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.
Building a whole Tailwind app, not just this component?
You just browsed 30 Tailwind text hover effects. Untitled UI Pro unlocks 5,000+ components and sections and 250+ page examples for React, styled with Tailwind CSS v4. One licence is a one-time $349 with lifetime updates; teams start at $999.
Four navbar underlines in one class list each: grow left→right, bloom from center, slide-through (enters left, exits right) and the native underline that docks onto the text with underline-offset. The first three are a single gradient sized with bg-[length:0%_0.09em] → hover:bg-[length:100%_0.09em], so links that wrap underline every line.
A hollow wordmark that floods with water on hover: an outlined layer ([-webkit-text-stroke:…]) under a gradient layer clipped with bg-clip-text and a typed @property level that tweens from 0% to 104%, then bobs. The nav pills below fill bottom-up with bg-[length:100%_0%] → hover:bg-[length:100%_100%].
Solid ink that crossfades into a live gradient: the gradient is always painted behind the glyphs with bg-clip-text, and hover just fades text-tth03-ink to text-transparent. Once revealed, a typed --tth03-angle keeps the gradient slowly turning. Each row sets its own two stops with a two-variable style.
A pen stroke drawn across text in three real contexts: a compare-at price that's crossed out while you aim at the buy row, a to-do list where group-has-checked/todo: draws the line with zero JS, and a sold-out link whose line enters left and exits right via not-hover:after:origin-right.
The agency-site nav where each word slides up and a twin slides in from below — here an English/Japanese pair — plus a real 3D rolodex: a transform-3d prism with two faces that rotates -90deg on X. Twins are aria-hidden, so screen readers hear one language.
A cyberpunk title that shreds on hover: two clones generated with before:content-[attr(data-text)] — one red, one cyan — sliced by clip-path keyframes on a steps() clock while the base jitters. Scanlines are an before: repeating gradient on the stage. Pure Tailwind: every keyframe lives in @theme.
Newsletter
Enjoying these demos? Get new ones by email.
A short email when a new collection ships. Unsubscribe any time.
A bar sign that ignites when you hover it: unlit glass tubes sputter on with a steps(1) keyframe, then hold a five-layer glow from v4's text-shadow-* theme namespace. The faulty W keeps buzzing, and the pill outline lights 180ms after the letters. One aria-label speaks the whole sign.
A membership card in gold leaf: the type is a 250%-wide gradient with a bright band in the middle, clipped to the glyphs with bg-clip-text. Hovering the card slides bg-[position:97%_0] to 3%_0, so the glint sweeps across the wordmark and price while the eyebrow keeps an ambient 6s glint.
A festival headline that pops out of the page: six same-length text-shadow layers interpolate from 0 to an extruded side, with active: pressing it back in on click. Matching neo-brutalist buttons lift and press with box-shadow. Everything rides a springy cubic-bezier(.34,1.8,.4,1).
A juice-brand wordmark whose letters jump in a wave: each letter sets style="--i:N", and one class list on the parent — *:delay-[calc(var(--i)*45ms)] — staggers them all. Because it's transitions, not keyframes, leaving mid-wave reverses through the same stagger. The flavour pills reuse the trick at a smaller amplitude.
A night-photography headline where the cursor is a torch: a city photo lives inside the glyphs (bg-clip-text), hidden by a radial mask-image centred at var(--tth11-x) var(--tth11-y). A 12-line script writes the coordinates; Tailwind does everything else, including the typed radius that grows from 0 to 170px on hover.
A pub-quiz card with answers blurred out: hover a row to peek, the Reveal pill pins it (keyboard and touch get equal rights), and a Reveal all switch unblurs every answer in a staggered cascade. All state is :has(:checked) — written as group-has-[[data-pin]:checked]/row: and group-has-[[data-all]:checked]/card:. Zero JS.
An editorial index where hovering one line pushes every other line out of focus. The whole effect is one stacked variant on each row — group-hover/list:not-hover:blur-[4px] — plus its keyboard twin group-has-focus-visible/list:not-has-focus-visible:. A hidden P.S. sits blurred below until you hover it.
Awwwards-style magnetic pills and a giant email link that lean toward the cursor. A tiny script writes --tth14-mx/--tth14-my; the markup reads them with translate-x-[var(--tth14-mx,0px)], and the label leans 55% further for parallax. Release snaps back on an elastic not-hover:ease-[cubic-bezier(.22,1.6,.36,1)].
Newsletter
Enjoying these demos? Get new ones by email.
A short email when a new collection ships. Unsubscribe any time.
A luxury-magazine masthead that breathes outward from a stable centre: tracking-[0.14em] → hover:tracking-[0.46em], with a matching negative right margin that swallows the phantom trailing space letter-spacing adds. The issue number does the opposite and tightens; the section nav expands subtly.
A foundry wordmark that pours from outline to solid: one element carries both the stroke ([-webkit-text-stroke:…]) and a gradient clipped to the glyphs whose height grows bg-[length:100%_0%] → hover:bg-[length:100%_100%] from the baseline. The nav bars fill left to right with the same two utilities rotated.
A terminal ops menu whose actions decrypt on hover: random glyphs cycle and lock left to right in 28ms ticks. Monospace type plus min-w-[16ch] widths keep rows from breathing as characters change, and each link carries a stable aria-label so screen readers never hear the noise.
Two ways to encase a CTA: a frame drawn by two corner pseudo-elements (width first, then height, so the corners chase and meet), and a conic “comet” that orbits the label on a typed @property angle. Both are complete class lists — the only CSS is the angle registration and its keyframe.
A kids' coding-camp wordmark where every letter squashes and stretches like rubber, staggered 40ms and recoloured per letter. The whole animation is group-hover/word:*:animate-tth19-rubber on the parent with *:[animation-delay:calc(var(--i)*40ms)]; the CTA squashes on hover and presses with active:.
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.
A Pride headline that turns into flowing rainbow on hover — generated from just two oklch stops with in oklch longer hue, so every hue is equally bright (no muddy sRGB greens). The gradient is 300% wide and travels exactly −300% per loop, so the flow is seamless. The CTA uses the same trick, always-on while hovered.
A conference wordmark that leans toward the cursor from any corner — without JavaScript. Four invisible quadrant zones are named peers (peer/tl…peer/br); the word reads peer-hover/tl:rotate-x-[11deg] peer-hover/tl:-rotate-y-[13deg] and a matching shadow that always falls away from the tilt. Keyboard focus plays a gentle sway.
Newsletter
Enjoying these demos? Get new ones by email.
A short email when a new collection ships. Unsubscribe any time.
A film title that dissolves into smoke: each letter drifts up, sideways and rotated by its own --tth23-dx/--tth23-dr, blurring to nothing in a 35ms stagger. Because it's transitions, pulling away mid-dissolve re-condenses the word along the same path. All from the parent: group-hover/title:*:-translate-y-[1.25em] *:blur-[.14em] *:opacity-0.
A hot-sauce wordmark that catches fire on hover: five upward-stacked text-shadow layers — yellow core to smoke — jittered by a steps(3) keyframe, plus a ±0.5° skew loop for heat haze. At rest a slow ember pulse breathes. Both hover animations ship as one --animate-tth24-blaze token.
You hold the lamp: every heading and card casts a shadow away from the cursor, longer and softer with distance, while a pool of light follows the pointer across the stage. The script writes --tth25-sx/sy/sb; the markup consumes them in [text-shadow:var(--tth25-sx,0px)_var(--tth25-sy,5px)_…] — and the fallbacks are the resting design.
Streetwear ticker tapes in counter-flow: two tilted strips loop with animate-tth26-roll and its reverse twin. Hovering a strip surges it to 4× with updatePlaybackRate(4) — no phase jump, which a CSS duration swap can't do. A real aria-pressed pause button styles itself with aria-pressed: and starts pressed for reduced-motion users.
A risograph poster headline printed in two plates: before: and after: both print attr(data-text), clipped to top and bottom halves. On hover the plates shear sideways and their clips overlap — with mix-blend-multiply, the overlap band overprints into a third ink, exactly like misregistered riso. Nav buttons split left/right.
Glossary tooltips for a pricing page: each dotted term is a real <button> whose bubble is after:content-[attr(data-tip)] with a CSS-triangle before: arrow. Hover waits 350ms (hover:after:delay-350) to ignore drive-by cursors; focus and tap show it instantly. Screen readers get the same text via aria-describedby.
A surf-report wordmark that slips underwater on hover: a hidden SVG filter (feTurbulence + feDisplacementMap, with an animated baseFrequency) distorts an aqua clone, applied with [filter:url(#tth29-ripple)]. Because filter:url() can't transition, the crisp word and the rippled clone crossfade instead.
Three title-sequence reveals for a motion studio's reel index: a diagonal polygon() wipe, a circle() bloom that uncovers a photo inside the letters, and 12-point venetian blinds. Each row is one class list: [clip-path:…] at rest, group-hover/row:[clip-path:…] on hover — same function, same point count, so it tweens.
FAQ
Frequently asked questions
How does Tailwind gradient text work with bg-clip-text, and why does mine show up as solid colour?
The gradient demos paint a gradient on the element, then punch the text through it. You give the heading a background with bg-gradient-to-r, add bg-clip-text so the paint only shows inside the glyph shapes, and set text-transparent so the real letters stop covering it. The crossfade in demo 03 keeps a solid ink layer on top and fades it out to reveal the gradient underneath, rather than animating the gradient in from nothing. The bug that bites everyone: forgetting text-transparent (or leaving a text-white on the element) — the clip still runs, but the opaque letters sit over it, so you see solid text and swear the clip failed. The animated rainbow in demo 21 moves a 200%-wide background with background-position; animating the colour stops themselves does nothing.
How do the staggered per-letter hover animations delay each character?
Each letter is its own element carrying an inline style="--i:N" custom property, and the shared transition reads that index to offset its delay — letter 0 fires first, letter 5 fires 5 * 40ms later, giving the wave in demos 10 and 19. That means you must split the word into one span per character by hand or in a loop; a single text node has nothing to stagger. Keep the wrapper as the hover target, not each letter, or the wave restarts whenever the cursor crosses a gap between glyphs. Render spaces as their own non-animated spans so the word still wraps naturally. The elastic demo 19 pairs the delay with a springy cubic-bezier and recolours per letter; the smoke demo 23 reads a per-letter --tth23-d for drift distance so no two letters leave on the same path.
Are these text hover effects keyboard accessible and safe for reduced-motion users?
Two things to check. First, anything that runs forever or moves a lot — the marquee in demo 26, the neon flicker in 07, the rainbow slide in 21 — should still under Tailwind's motion-reduce: variant; set motion-reduce:animate-none and give it a readable resting state. Second, never hide meaning behind hover alone. The spoiler in demo 12 and the tooltip in demo 28 are built on a real <button>, so they open on focus and on Enter or Space, not just pointer hover — a keyboard or touch user can still reach them. Purely decorative effects like the 3D pop or the gold shimmer are fine hover-only, because the text is already fully readable at rest. The reduced-motion and focus rules only bite when the hover either moves continuously or reveals content the user actually needs.
When should I not use these text hover effects?
Keep them on headings, nav labels, wordmarks and CTAs — short, large, deliberately-hovered type. Do not apply them to body copy or long paragraphs. bg-clip-text with text-transparent removes the real text colour, which weakens contrast at paragraph sizes; the hollow stroke state in demo 16 is worse, outlined letters are hard to read as running text. Per-letter effects — waves, scramble, rubber band — need every character wrapped in a span, which bloats the DOM and breaks selection and copy/paste over a sentence. The cursor-driven ones — magnetic 14, spotlight 11, dynamic shadow 25 — run a pointer handler on every move; one hero is fine, a page full of them is not. And hover does nothing on touch screens, so never let an effect be the only way to reach real information.
How is the glitch text effect built without duplicating the text three times in the markup?
The glitch in demo 06 stacks three copies of the same string. The base element holds the real text; two pseudo-elements, before: and after:, pull the same string from a data-text attribute with before:content-[attr(data-text)], so you never retype the words in markup. Each clone is tinted — one cyan, one magenta — offset a few pixels, and masked with a clip-path that jumps between keyframes, so slices of the clones flash out of register for the chromatic-aberration look. The thing that trips people: the data-text attribute must actually be present on the element, or both pseudo-elements render empty and you get a plain title with no glitch. The clones live in pseudo-elements, so screen readers only announce the single base string and never read the effect twice.
Why can't I smoothly transition the outline-to-fill text stroke effect?
The outline-to-fill demos (02 and 16) put both a stroke and a fill on one element using [-webkit-text-stroke:2px_currentColor] plus a transparent or coloured fill. The catch is that -webkit-text-stroke is not an animatable property — you cannot ease the stroke width, and flipping text-transparent to a solid colour snaps rather than eases. So the fill is faked: demo 02 slides a filled layer up behind the hollow one like water rising, and demo 16 crossfades a solid-coloured duplicate over the outlined base. Support is wide but still vendor-prefixed, so always ship the -webkit-text-stroke form; there is no unprefixed version in general use. Give the hollow resting state a bold or heavy face, or thin strokes thin out and vanish on low-DPI screens.
Which of these effects need JavaScript and which are pure CSS?
Most of the collection is pure CSS — the perspective tilt in demo 22, for instance, fakes cursor tracking with four invisible quadrant hover zones and needs no script at all. JavaScript appears only where the effect must read the live cursor position or rewrite the text. The magnetic pills in demo 14 write the pointer's offset into --x and --y custom properties so the element leans toward the cursor; the spotlight in 11 and the dynamic shadow in 25 do the same from a pointermove handler. The scramble in demo 17 swaps random glyphs on a timer and locks them left to right, which no static CSS can do. When you copy one of those, keep the handler light — update a custom property and let CSS paint, rather than writing style.transform every frame — and bind the listener to the element, not the whole document, so idle hovers cost nothing.
How is the hover tooltip built with no JavaScript, and how do I make it accessible?
The tooltip in demo 28 uses no script. Each term is a real <button> and the bubble is a sibling revealed with group-state utilities — group-hover: and group-focus: on the wrapper flip the bubble from opacity-0 to opacity-100. Because the trigger is a button, the tip appears on keyboard focus as well as hover, which a plain <div> trigger cannot do. Point the term at its bubble with aria-describedby so assistive tech announces the definition when the control is focused, and keep the bubble in the DOM dimmed rather than at display:none, so that association stays live. Position it absolute inside a relative wrapper, and let it flip above or below near an edge, or the first row of a glossary clips its tip against the top of the card.