Pure TailwindMIT licensed

Tailwind Inline Word Swap Hover Effect

Six ways to replace a word on hover — slide up, clip reveal, cross-fade, scale-in, blur swap and a flip-down. Two absolutely stacked spans, one leaving and one arriving, with the incoming word marked aria-hidden.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<section class="thv-28 w-full min-h-svh bg-thv28-bg font-thv28-sans px-8 py-12 flex flex-col items-start gap-8 max-w-[640px] mx-auto">
  <p class="self-center text-[0.72rem] tracking-[0.15em] uppercase text-thv28-dim">Inline Word Swap — 6 Styles</p>

  <div class="flex items-center gap-6">
    <a href="#" aria-label="Design Systems" class="group relative inline-block overflow-hidden min-w-[8ch] h-[1.2em] text-[1.6rem] font-bold leading-[1.2] no-underline align-bottom rounded-xs focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-thv28-acc">
      <span class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-ink transition-transform duration-300 ease-[cubic-bezier(.4,0,.2,1)] group-hover:-translate-y-full group-focus-visible:-translate-y-full motion-reduce:transition-none">Design</span>
      <span aria-hidden="true" class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-acc translate-y-full transition-transform duration-300 ease-[cubic-bezier(.4,0,.2,1)] group-hover:translate-y-0 group-focus-visible:translate-y-0 motion-reduce:transition-none">Systems</span>
    </a>
    <span class="min-w-[90px] text-[0.68rem] tracking-[0.1em] uppercase text-thv28-dim">Slide Up</span>
  </div>

  <div class="flex items-center gap-6">
    <a href="#" aria-label="Make it Work" class="group relative inline-block overflow-hidden min-w-[8ch] h-[1.2em] text-[1.6rem] font-bold leading-[1.2] no-underline align-bottom rounded-xs focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-thv28-acc">
      <span class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-ink [clip-path:inset(0_0_0_0)] transition-[clip-path] duration-300 ease-[cubic-bezier(.4,0,.2,1)] group-hover:[clip-path:inset(0_0_0_100%)] group-focus-visible:[clip-path:inset(0_0_0_100%)] motion-reduce:transition-none">Make it</span>
      <span aria-hidden="true" class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-acc [clip-path:inset(0_100%_0_0)] transition-[clip-path] duration-300 ease-[cubic-bezier(.4,0,.2,1)] group-hover:[clip-path:inset(0_0_0_0)] group-focus-visible:[clip-path:inset(0_0_0_0)] motion-reduce:transition-none">Work</span>
    </a>
    <span class="min-w-[90px] text-[0.68rem] tracking-[0.1em] uppercase text-thv28-dim">Clip Reveal</span>
  </div>

  <div class="flex items-center gap-6">
    <a href="#" aria-label="Start Now" class="group relative inline-block overflow-hidden min-w-[8ch] h-[1.2em] text-[1.6rem] font-bold leading-[1.2] no-underline align-bottom rounded-xs focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-thv28-acc">
      <span class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-ink transition-opacity duration-250 group-hover:opacity-0 group-focus-visible:opacity-0">Start</span>
      <span aria-hidden="true" class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-acc opacity-0 transition-opacity duration-250 delay-100 group-hover:opacity-100 group-focus-visible:opacity-100">Now</span>
    </a>
    <span class="min-w-[90px] text-[0.68rem] tracking-[0.1em] uppercase text-thv28-dim">Fade Cross</span>
  </div>

  <div class="flex items-center gap-6">
    <a href="#" aria-label="Build Fast" class="group relative inline-block overflow-hidden min-w-[8ch] h-[1.2em] text-[1.6rem] font-bold leading-[1.2] no-underline align-bottom rounded-xs focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-thv28-acc">
      <span class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-ink transition-[opacity,transform] duration-250 group-hover:opacity-0 group-hover:scale-125 group-focus-visible:opacity-0 motion-reduce:transition-opacity">Build</span>
      <span aria-hidden="true" class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-acc opacity-0 scale-[.6] transition-[opacity,transform] duration-300 delay-[50ms] ease-[cubic-bezier(.34,1.56,.64,1)] group-hover:opacity-100 group-hover:scale-100 group-focus-visible:opacity-100 group-focus-visible:scale-100 motion-reduce:transition-opacity">Fast</span>
    </a>
    <span class="min-w-[90px] text-[0.68rem] tracking-[0.1em] uppercase text-thv28-dim">Scale In</span>
  </div>

  <div class="flex items-center gap-6">
    <a href="#" aria-label="Ship it" class="group relative inline-block overflow-hidden min-w-[8ch] h-[1.2em] text-[1.6rem] font-bold leading-[1.2] no-underline align-bottom rounded-xs focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-thv28-acc">
      <span class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-ink transition-[filter,opacity] duration-300 group-hover:blur-[8px] group-hover:opacity-0 group-focus-visible:opacity-0">Ship</span>
      <span aria-hidden="true" class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-acc blur-[8px] opacity-0 transition-[filter,opacity] duration-300 delay-[50ms] group-hover:blur-0 group-hover:opacity-100 group-focus-visible:blur-0 group-focus-visible:opacity-100">it</span>
    </a>
    <span class="min-w-[90px] text-[0.68rem] tracking-[0.1em] uppercase text-thv28-dim">Blur Swap</span>
  </div>

  <div class="flex items-center gap-6">
    <a href="#" aria-label="Create Iterate" class="group relative inline-block overflow-hidden min-w-[8ch] h-[1.2em] perspective-[300px] text-[1.6rem] font-bold leading-[1.2] no-underline align-bottom rounded-xs focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-thv28-acc">
      <span class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-ink origin-bottom transition-[transform,opacity] duration-350 ease-[cubic-bezier(.4,0,.2,1)] group-hover:rotate-x-90 group-hover:opacity-0 group-focus-visible:rotate-x-90 group-focus-visible:opacity-0 motion-reduce:transition-opacity">Create</span>
      <span aria-hidden="true" class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-acc origin-top rotate-x-[-90deg] opacity-0 transition-[transform,opacity] duration-350 delay-[50ms] ease-[cubic-bezier(.4,0,.2,1)] group-hover:rotate-x-0 group-hover:opacity-100 group-focus-visible:rotate-x-0 group-focus-visible:opacity-100 motion-reduce:transition-opacity">Iterate</span>
    </a>
    <span class="min-w-[90px] text-[0.68rem] tracking-[0.1em] uppercase text-thv28-dim">Flip Down</span>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-thv28-bg: #0b0b0e;
  --color-thv28-bg: oklch(0.12 0.01 285);
  --color-thv28-ink: #d5d5da;
  --color-thv28-ink: oklch(0.86 0.01 285);
  --color-thv28-dim: #8a8a96;
  --color-thv28-dim: oklch(0.64 0.01 285);
  --color-thv28-acc: #7c6af7;
  --color-thv28-acc: oklch(0.62 0.2 285);
  --font-thv28-sans: 'Space Grotesk', system-ui, sans-serif;
}

.thv-28 {
  width: 100%;
  min-height: 100svh;
}

.thv-28 ::selection {
  background: var(--color-thv28-acc);
  color: #fff;
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<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.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-thv28-bg: #0b0b0e;
  --color-thv28-bg: oklch(0.12 0.01 285);
  --color-thv28-ink: #d5d5da;
  --color-thv28-ink: oklch(0.86 0.01 285);
  --color-thv28-dim: #8a8a96;
  --color-thv28-dim: oklch(0.64 0.01 285);
  --color-thv28-acc: #7c6af7;
  --color-thv28-acc: oklch(0.62 0.2 285);
  --font-thv28-sans: 'Space Grotesk', system-ui, sans-serif;
}

.thv-28 {
  width: 100%;
  min-height: 100svh;
}

.thv-28 ::selection {
  background: var(--color-thv28-acc);
  color: #fff;
}
3Fonts used by this demooptional — skip if you have your own type stack
<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.

  • Outgoing and incoming states are two sibling class lists you can read side by side, which is the cheapest way to keep a swap symmetrical.
  • UI change from the CSS original: every pair swaps on group-focus-visible: too, and the source's aria-label="Ship Ship" (which swapped a word for itself) is fixed to a real pair.
  • Under motion-reduce: the transforms are dropped but the cross-fade remains, so the second word still arrives.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
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 Inline Word Swap Hover Effect
Source: https://codefronts.com/motion/tailwind-css-hover-effects/tailwind-inline-word-swap-hover-effect/

Six ways to replace a word on hover — slide up, clip reveal, cross-fade, scale-in, blur swap and a flip-down. Two absolutely stacked spans, one leaving and one arriving, with the incoming word marked aria-hidden.
## HTML
```html
<section class="thv-28 w-full min-h-svh bg-thv28-bg font-thv28-sans px-8 py-12 flex flex-col items-start gap-8 max-w-[640px] mx-auto">
  <p class="self-center text-[0.72rem] tracking-[0.15em] uppercase text-thv28-dim">Inline Word Swap — 6 Styles</p>

  <div class="flex items-center gap-6">
    <a href="#" aria-label="Design Systems" class="group relative inline-block overflow-hidden min-w-[8ch] h-[1.2em] text-[1.6rem] font-bold leading-[1.2] no-underline align-bottom rounded-xs focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-thv28-acc">
      <span class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-ink transition-transform duration-300 ease-[cubic-bezier(.4,0,.2,1)] group-hover:-translate-y-full group-focus-visible:-translate-y-full motion-reduce:transition-none">Design</span>
      <span aria-hidden="true" class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-acc translate-y-full transition-transform duration-300 ease-[cubic-bezier(.4,0,.2,1)] group-hover:translate-y-0 group-focus-visible:translate-y-0 motion-reduce:transition-none">Systems</span>
    </a>
    <span class="min-w-[90px] text-[0.68rem] tracking-[0.1em] uppercase text-thv28-dim">Slide Up</span>
  </div>

  <div class="flex items-center gap-6">
    <a href="#" aria-label="Make it Work" class="group relative inline-block overflow-hidden min-w-[8ch] h-[1.2em] text-[1.6rem] font-bold leading-[1.2] no-underline align-bottom rounded-xs focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-thv28-acc">
      <span class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-ink [clip-path:inset(0_0_0_0)] transition-[clip-path] duration-300 ease-[cubic-bezier(.4,0,.2,1)] group-hover:[clip-path:inset(0_0_0_100%)] group-focus-visible:[clip-path:inset(0_0_0_100%)] motion-reduce:transition-none">Make it</span>
      <span aria-hidden="true" class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-acc [clip-path:inset(0_100%_0_0)] transition-[clip-path] duration-300 ease-[cubic-bezier(.4,0,.2,1)] group-hover:[clip-path:inset(0_0_0_0)] group-focus-visible:[clip-path:inset(0_0_0_0)] motion-reduce:transition-none">Work</span>
    </a>
    <span class="min-w-[90px] text-[0.68rem] tracking-[0.1em] uppercase text-thv28-dim">Clip Reveal</span>
  </div>

  <div class="flex items-center gap-6">
    <a href="#" aria-label="Start Now" class="group relative inline-block overflow-hidden min-w-[8ch] h-[1.2em] text-[1.6rem] font-bold leading-[1.2] no-underline align-bottom rounded-xs focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-thv28-acc">
      <span class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-ink transition-opacity duration-250 group-hover:opacity-0 group-focus-visible:opacity-0">Start</span>
      <span aria-hidden="true" class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-acc opacity-0 transition-opacity duration-250 delay-100 group-hover:opacity-100 group-focus-visible:opacity-100">Now</span>
    </a>
    <span class="min-w-[90px] text-[0.68rem] tracking-[0.1em] uppercase text-thv28-dim">Fade Cross</span>
  </div>

  <div class="flex items-center gap-6">
    <a href="#" aria-label="Build Fast" class="group relative inline-block overflow-hidden min-w-[8ch] h-[1.2em] text-[1.6rem] font-bold leading-[1.2] no-underline align-bottom rounded-xs focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-thv28-acc">
      <span class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-ink transition-[opacity,transform] duration-250 group-hover:opacity-0 group-hover:scale-125 group-focus-visible:opacity-0 motion-reduce:transition-opacity">Build</span>
      <span aria-hidden="true" class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-acc opacity-0 scale-[.6] transition-[opacity,transform] duration-300 delay-[50ms] ease-[cubic-bezier(.34,1.56,.64,1)] group-hover:opacity-100 group-hover:scale-100 group-focus-visible:opacity-100 group-focus-visible:scale-100 motion-reduce:transition-opacity">Fast</span>
    </a>
    <span class="min-w-[90px] text-[0.68rem] tracking-[0.1em] uppercase text-thv28-dim">Scale In</span>
  </div>

  <div class="flex items-center gap-6">
    <a href="#" aria-label="Ship it" class="group relative inline-block overflow-hidden min-w-[8ch] h-[1.2em] text-[1.6rem] font-bold leading-[1.2] no-underline align-bottom rounded-xs focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-thv28-acc">
      <span class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-ink transition-[filter,opacity] duration-300 group-hover:blur-[8px] group-hover:opacity-0 group-focus-visible:opacity-0">Ship</span>
      <span aria-hidden="true" class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-acc blur-[8px] opacity-0 transition-[filter,opacity] duration-300 delay-[50ms] group-hover:blur-0 group-hover:opacity-100 group-focus-visible:blur-0 group-focus-visible:opacity-100">it</span>
    </a>
    <span class="min-w-[90px] text-[0.68rem] tracking-[0.1em] uppercase text-thv28-dim">Blur Swap</span>
  </div>

  <div class="flex items-center gap-6">
    <a href="#" aria-label="Create Iterate" class="group relative inline-block overflow-hidden min-w-[8ch] h-[1.2em] perspective-[300px] text-[1.6rem] font-bold leading-[1.2] no-underline align-bottom rounded-xs focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-thv28-acc">
      <span class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-ink origin-bottom transition-[transform,opacity] duration-350 ease-[cubic-bezier(.4,0,.2,1)] group-hover:rotate-x-90 group-hover:opacity-0 group-focus-visible:rotate-x-90 group-focus-visible:opacity-0 motion-reduce:transition-opacity">Create</span>
      <span aria-hidden="true" class="absolute inset-x-0 top-0 block whitespace-nowrap text-thv28-acc origin-top rotate-x-[-90deg] opacity-0 transition-[transform,opacity] duration-350 delay-[50ms] ease-[cubic-bezier(.4,0,.2,1)] group-hover:rotate-x-0 group-hover:opacity-100 group-focus-visible:rotate-x-0 group-focus-visible:opacity-100 motion-reduce:transition-opacity">Iterate</span>
    </a>
    <span class="min-w-[90px] text-[0.68rem] tracking-[0.1em] uppercase text-thv28-dim">Flip Down</span>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-thv28-bg: #0b0b0e;
  --color-thv28-bg: oklch(0.12 0.01 285);
  --color-thv28-ink: #d5d5da;
  --color-thv28-ink: oklch(0.86 0.01 285);
  --color-thv28-dim: #8a8a96;
  --color-thv28-dim: oklch(0.64 0.01 285);
  --color-thv28-acc: #7c6af7;
  --color-thv28-acc: oklch(0.62 0.2 285);
  --font-thv28-sans: 'Space Grotesk', system-ui, sans-serif;
}

.thv-28 {
  width: 100%;
  min-height: 100svh;
}

.thv-28 ::selection {
  background: var(--color-thv28-acc);
  color: #fff;
}
```

How this works

Two spans, one box. The link is relative inline-block overflow-hidden with a fixed h-[1.2em] and a min-w-[8ch]; both words are absolute inset-x-0 top-0, so no layout shift can happen between a short word and a long one.

Six exits, six entrances. Each variant pairs an outgoing transform with its complement — up/up, clip-right/clip-left, fade/fade, scale-up/scale-down, blur/unblur, rotate-away/rotate-in.

Accessibility is markup, not motion. The link carries an aria-label naming both words; the swap span is aria-hidden. A screen reader hears one coherent label instead of two overlapping words.

Make it yours

  • Swap in your own pairs — keep them similar in length or raise min-w-*.
  • Slot-machine feel: add group-hover:tracking-[0.1em] to the incoming word.
  • Speed: duration-200 reads snappy, duration-500 reads considered.

Gotchas — read before shipping

  • Without a fixed width the link jumps as the words swap; min-w-[8ch] is load-bearing.
  • clip-path on an inline element does nothing — the words are block.
  • Keep the aria-label in sync with both words, or the accessible name drifts from what is on screen.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Transforms, clip-path and filter are Baseline; the floor is Tailwind v4's generated sheet.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Arbitraryneeds bracket syntax

Search CodeFronts

Loading…