30 Tailwind CSS Text Hover Effects
05 / 30
Pure TailwindMIT licensed

Tailwind Text Swap and Flip Up Hover Effect

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.

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="tth-05 w-full min-h-svh grid place-items-center bg-tth05-bg px-5 py-14 font-tth05-sans text-tth05-ink" aria-label="Text swap and flip up demo">
  <div class="flex w-full max-w-[47rem] flex-col gap-7">
    <header class="flex flex-wrap items-center justify-between gap-3.5 border-b-[1.5px] border-tth05-ink pb-3.5">
      <p class="text-xs font-bold uppercase tracking-[0.22em] text-tth05-mut">Atelier Kōyō &mdash; Kyoto / Melbourne</p>
      <span class="rounded-full border border-tth05-acc/40 px-3 py-1.5 font-tth05-mono text-[11px] text-tth05-acc">EN &harr; JA on hover</span>
    </header>
    <nav class="flex flex-wrap gap-[clamp(6px,1.6vw,14px)]" aria-label="Primary">
      <a id="tth05-top" href="#tth05-top" class="group/swap relative inline-block overflow-clip rounded-lg px-[0.45em] py-[0.3em] text-[clamp(26px,5.4vw,44px)] font-extrabold leading-[1.15] tracking-[-0.02em] no-underline transition-colors duration-300 hover:bg-tth05-ink/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tth05-acc"><span class="block transition-transform duration-450 ease-[cubic-bezier(.76,0,.24,1)] group-hover/swap:-translate-y-[150%] group-focus-visible/swap:-translate-y-[150%] motion-reduce:transition-none">Works</span><span aria-hidden="true" lang="ja" class="absolute inset-x-[0.45em] inset-y-[0.3em] block translate-y-[150%] font-tth05-ja text-tth05-acc transition-transform duration-450 ease-[cubic-bezier(.76,0,.24,1)] group-hover/swap:translate-y-0 group-focus-visible/swap:translate-y-0 motion-reduce:transition-none">作品</span></a>
      <a href="#tth05-top" class="group/swap relative inline-block overflow-clip rounded-lg px-[0.45em] py-[0.3em] text-[clamp(26px,5.4vw,44px)] font-extrabold leading-[1.15] tracking-[-0.02em] no-underline transition-colors duration-300 hover:bg-tth05-ink/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tth05-acc"><span class="block transition-transform duration-450 ease-[cubic-bezier(.76,0,.24,1)] group-hover/swap:-translate-y-[150%] group-focus-visible/swap:-translate-y-[150%] motion-reduce:transition-none">Studio</span><span aria-hidden="true" lang="ja" class="absolute inset-x-[0.45em] inset-y-[0.3em] block translate-y-[150%] font-tth05-ja text-tth05-acc transition-transform duration-450 ease-[cubic-bezier(.76,0,.24,1)] group-hover/swap:translate-y-0 group-focus-visible/swap:translate-y-0 motion-reduce:transition-none">工房</span></a>
      <a href="#tth05-top" class="group/swap relative inline-block overflow-clip rounded-lg px-[0.45em] py-[0.3em] text-[clamp(26px,5.4vw,44px)] font-extrabold leading-[1.15] tracking-[-0.02em] no-underline transition-colors duration-300 hover:bg-tth05-ink/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tth05-acc"><span class="block transition-transform duration-450 ease-[cubic-bezier(.76,0,.24,1)] group-hover/swap:-translate-y-[150%] group-focus-visible/swap:-translate-y-[150%] motion-reduce:transition-none">Journal</span><span aria-hidden="true" lang="ja" class="absolute inset-x-[0.45em] inset-y-[0.3em] block translate-y-[150%] font-tth05-ja text-tth05-acc transition-transform duration-450 ease-[cubic-bezier(.76,0,.24,1)] group-hover/swap:translate-y-0 group-focus-visible/swap:translate-y-0 motion-reduce:transition-none">日誌</span></a>
      <a href="#tth05-top" class="group/swap relative inline-block overflow-clip rounded-lg px-[0.45em] py-[0.3em] text-[clamp(26px,5.4vw,44px)] font-extrabold leading-[1.15] tracking-[-0.02em] no-underline transition-colors duration-300 hover:bg-tth05-ink/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tth05-acc"><span class="block transition-transform duration-450 ease-[cubic-bezier(.76,0,.24,1)] group-hover/swap:-translate-y-[150%] group-focus-visible/swap:-translate-y-[150%] motion-reduce:transition-none">Contact</span><span aria-hidden="true" lang="ja" class="absolute inset-x-[0.45em] inset-y-[0.3em] block translate-y-[150%] font-tth05-ja text-tth05-acc transition-transform duration-450 ease-[cubic-bezier(.76,0,.24,1)] group-hover/swap:translate-y-0 group-focus-visible/swap:translate-y-0 motion-reduce:transition-none">連絡</span></a>
    </nav>
    <div class="flex flex-wrap items-center gap-[clamp(14px,3vw,24px)]">
      <a href="#tth05-top" aria-label="Commissions, open for autumn" class="group/roll inline-flex min-h-12 items-center rounded-full border-[1.5px] border-tth05-ink px-6 no-underline perspective-[900px] transition-colors duration-300 hover:border-tth05-acc hover:bg-tth05-acc/8 focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-tth05-acc">
        <span aria-hidden="true" class="relative block h-[1em] text-[clamp(19px,3vw,25px)] font-bold leading-none transform-3d transition-transform duration-550 ease-[cubic-bezier(.68,-0.15,.27,1.15)] group-hover/roll:rotate-x-[-90deg] group-focus-visible/roll:rotate-x-[-90deg] motion-reduce:transition-none">
          <span class="flex h-[1em] items-center whitespace-nowrap text-tth05-ink translate-z-[0.52em] backface-hidden">Commissions</span>
          <span class="absolute inset-0 flex h-[1em] items-center whitespace-nowrap text-tth05-acc rotate-x-90 translate-z-[0.52em] backface-hidden">Open for autumn</span>
        </span>
      </a>
      <p class="text-[13px] text-tth05-mut">&larr; the rolodex: a real <code class="rounded bg-tth05-ink/7 px-1.5 py-px font-tth05-mono text-[0.9em]">rotate-x</code> prism, not a slide</p>
    </div>
    <p class="text-[12.5px] text-tth05-mut">Hover or <kbd class="rounded-[5px] border border-b-2 border-tth05-line bg-white px-1.5 py-0.5 font-tth05-mono text-[11px]">Tab</kbd>. The Japanese twins are <code class="rounded bg-tth05-ink/7 px-1.5 py-px font-tth05-mono text-[0.9em]">aria-hidden</code> &mdash; screen readers hear one language, not both.</p>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-tth05-bg: #f5f6f1;
  --color-tth05-bg: oklch(0.968 0.005 110);
  --color-tth05-ink: #1d211b;
  --color-tth05-ink: oklch(0.23 0.012 130);
  --color-tth05-mut: #5b6157;
  --color-tth05-mut: oklch(0.47 0.015 125);
  --color-tth05-acc: #2d8a4a;
  --color-tth05-acc: oklch(0.54 0.15 145);
  --color-tth05-line: #dcdfd6;
  --color-tth05-line: oklch(0.885 0.01 115);
  --font-tth05-sans: 'Syne', system-ui, sans-serif;
  --font-tth05-ja: 'Zen Kaku Gothic New', 'Hiragino Sans', sans-serif;
  --font-tth05-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tth-05 {
  width: 100%;
  min-height: 100svh;
}

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-tth05-bg: #f5f6f1;
  --color-tth05-bg: oklch(0.968 0.005 110);
  --color-tth05-ink: #1d211b;
  --color-tth05-ink: oklch(0.23 0.012 130);
  --color-tth05-mut: #5b6157;
  --color-tth05-mut: oklch(0.47 0.015 125);
  --color-tth05-acc: #2d8a4a;
  --color-tth05-acc: oklch(0.54 0.15 145);
  --color-tth05-line: #dcdfd6;
  --color-tth05-line: oklch(0.885 0.01 115);
  --font-tth05-sans: 'Syne', system-ui, sans-serif;
  --font-tth05-ja: 'Zen Kaku Gothic New', 'Hiragino Sans', sans-serif;
  --font-tth05-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tth-05 {
  width: 100%;
  min-height: 100svh;
}
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=Syne:wght@500..800&family=Zen+Kaku+Gothic+New:wght@500;700&family=JetBrains+Mono:wght@400;600&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • transform-style:preserve-3d, backface-visibility and the rotateX()/translateZ() chain became v4's native transform-3d, backface-hidden, rotate-x-90 and translate-z-[0.52em].
  • Descendant hover rules became group-hover/swap: and group-hover/roll:, named so the nested groups never cross-fire.
  • UI upgrade from the CSS original: the rolodex is now a 48px pill button with a border that tints on hover — the bare text before gave no hint it was clickable.
  • UI upgrade: the Japanese twins use Zen Kaku Gothic New (set with lang="ja"), so kanji no longer fall back to a mismatched system face beside Syne.
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 Text Swap and Flip Up Hover Effect
Source: https://codefronts.com/motion/tailwind-css-text-hover-effects/tailwind-text-swap-and-flip-up-hover-effect/

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.
## HTML
```html
<section class="tth-05 w-full min-h-svh grid place-items-center bg-tth05-bg px-5 py-14 font-tth05-sans text-tth05-ink" aria-label="Text swap and flip up demo">
  <div class="flex w-full max-w-[47rem] flex-col gap-7">
    <header class="flex flex-wrap items-center justify-between gap-3.5 border-b-[1.5px] border-tth05-ink pb-3.5">
      <p class="text-xs font-bold uppercase tracking-[0.22em] text-tth05-mut">Atelier Kōyō &mdash; Kyoto / Melbourne</p>
      <span class="rounded-full border border-tth05-acc/40 px-3 py-1.5 font-tth05-mono text-[11px] text-tth05-acc">EN &harr; JA on hover</span>
    </header>
    <nav class="flex flex-wrap gap-[clamp(6px,1.6vw,14px)]" aria-label="Primary">
      <a id="tth05-top" href="#tth05-top" class="group/swap relative inline-block overflow-clip rounded-lg px-[0.45em] py-[0.3em] text-[clamp(26px,5.4vw,44px)] font-extrabold leading-[1.15] tracking-[-0.02em] no-underline transition-colors duration-300 hover:bg-tth05-ink/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tth05-acc"><span class="block transition-transform duration-450 ease-[cubic-bezier(.76,0,.24,1)] group-hover/swap:-translate-y-[150%] group-focus-visible/swap:-translate-y-[150%] motion-reduce:transition-none">Works</span><span aria-hidden="true" lang="ja" class="absolute inset-x-[0.45em] inset-y-[0.3em] block translate-y-[150%] font-tth05-ja text-tth05-acc transition-transform duration-450 ease-[cubic-bezier(.76,0,.24,1)] group-hover/swap:translate-y-0 group-focus-visible/swap:translate-y-0 motion-reduce:transition-none">作品</span></a>
      <a href="#tth05-top" class="group/swap relative inline-block overflow-clip rounded-lg px-[0.45em] py-[0.3em] text-[clamp(26px,5.4vw,44px)] font-extrabold leading-[1.15] tracking-[-0.02em] no-underline transition-colors duration-300 hover:bg-tth05-ink/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tth05-acc"><span class="block transition-transform duration-450 ease-[cubic-bezier(.76,0,.24,1)] group-hover/swap:-translate-y-[150%] group-focus-visible/swap:-translate-y-[150%] motion-reduce:transition-none">Studio</span><span aria-hidden="true" lang="ja" class="absolute inset-x-[0.45em] inset-y-[0.3em] block translate-y-[150%] font-tth05-ja text-tth05-acc transition-transform duration-450 ease-[cubic-bezier(.76,0,.24,1)] group-hover/swap:translate-y-0 group-focus-visible/swap:translate-y-0 motion-reduce:transition-none">工房</span></a>
      <a href="#tth05-top" class="group/swap relative inline-block overflow-clip rounded-lg px-[0.45em] py-[0.3em] text-[clamp(26px,5.4vw,44px)] font-extrabold leading-[1.15] tracking-[-0.02em] no-underline transition-colors duration-300 hover:bg-tth05-ink/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tth05-acc"><span class="block transition-transform duration-450 ease-[cubic-bezier(.76,0,.24,1)] group-hover/swap:-translate-y-[150%] group-focus-visible/swap:-translate-y-[150%] motion-reduce:transition-none">Journal</span><span aria-hidden="true" lang="ja" class="absolute inset-x-[0.45em] inset-y-[0.3em] block translate-y-[150%] font-tth05-ja text-tth05-acc transition-transform duration-450 ease-[cubic-bezier(.76,0,.24,1)] group-hover/swap:translate-y-0 group-focus-visible/swap:translate-y-0 motion-reduce:transition-none">日誌</span></a>
      <a href="#tth05-top" class="group/swap relative inline-block overflow-clip rounded-lg px-[0.45em] py-[0.3em] text-[clamp(26px,5.4vw,44px)] font-extrabold leading-[1.15] tracking-[-0.02em] no-underline transition-colors duration-300 hover:bg-tth05-ink/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tth05-acc"><span class="block transition-transform duration-450 ease-[cubic-bezier(.76,0,.24,1)] group-hover/swap:-translate-y-[150%] group-focus-visible/swap:-translate-y-[150%] motion-reduce:transition-none">Contact</span><span aria-hidden="true" lang="ja" class="absolute inset-x-[0.45em] inset-y-[0.3em] block translate-y-[150%] font-tth05-ja text-tth05-acc transition-transform duration-450 ease-[cubic-bezier(.76,0,.24,1)] group-hover/swap:translate-y-0 group-focus-visible/swap:translate-y-0 motion-reduce:transition-none">連絡</span></a>
    </nav>
    <div class="flex flex-wrap items-center gap-[clamp(14px,3vw,24px)]">
      <a href="#tth05-top" aria-label="Commissions, open for autumn" class="group/roll inline-flex min-h-12 items-center rounded-full border-[1.5px] border-tth05-ink px-6 no-underline perspective-[900px] transition-colors duration-300 hover:border-tth05-acc hover:bg-tth05-acc/8 focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-tth05-acc">
        <span aria-hidden="true" class="relative block h-[1em] text-[clamp(19px,3vw,25px)] font-bold leading-none transform-3d transition-transform duration-550 ease-[cubic-bezier(.68,-0.15,.27,1.15)] group-hover/roll:rotate-x-[-90deg] group-focus-visible/roll:rotate-x-[-90deg] motion-reduce:transition-none">
          <span class="flex h-[1em] items-center whitespace-nowrap text-tth05-ink translate-z-[0.52em] backface-hidden">Commissions</span>
          <span class="absolute inset-0 flex h-[1em] items-center whitespace-nowrap text-tth05-acc rotate-x-90 translate-z-[0.52em] backface-hidden">Open for autumn</span>
        </span>
      </a>
      <p class="text-[13px] text-tth05-mut">&larr; the rolodex: a real <code class="rounded bg-tth05-ink/7 px-1.5 py-px font-tth05-mono text-[0.9em]">rotate-x</code> prism, not a slide</p>
    </div>
    <p class="text-[12.5px] text-tth05-mut">Hover or <kbd class="rounded-[5px] border border-b-2 border-tth05-line bg-white px-1.5 py-0.5 font-tth05-mono text-[11px]">Tab</kbd>. The Japanese twins are <code class="rounded bg-tth05-ink/7 px-1.5 py-px font-tth05-mono text-[0.9em]">aria-hidden</code> &mdash; screen readers hear one language, not both.</p>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tth05-bg: #f5f6f1;
  --color-tth05-bg: oklch(0.968 0.005 110);
  --color-tth05-ink: #1d211b;
  --color-tth05-ink: oklch(0.23 0.012 130);
  --color-tth05-mut: #5b6157;
  --color-tth05-mut: oklch(0.47 0.015 125);
  --color-tth05-acc: #2d8a4a;
  --color-tth05-acc: oklch(0.54 0.15 145);
  --color-tth05-line: #dcdfd6;
  --color-tth05-line: oklch(0.885 0.01 115);
  --font-tth05-sans: 'Syne', system-ui, sans-serif;
  --font-tth05-ja: 'Zen Kaku Gothic New', 'Hiragino Sans', sans-serif;
  --font-tth05-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tth-05 {
  width: 100%;
  min-height: 100svh;
}
```

How this works

The swap is two stacked spans inside an overflow-clip link. At rest the twin sits at translate-y-[150%]; on hover the original goes to -translate-y-[150%] and the twin to zero, both with the same expo-in-out curve so they move as one strip.

The prism uses v4's first-class 3D utilities:

<a class="group/roll perspective-[900px]">
  <span class="transform-3d transition-transform
    group-hover/roll:rotate-x-[-90deg]">
    <span class="translate-z-[0.52em] backface-hidden">Commissions</span>
    <span class="absolute inset-0 rotate-x-90 translate-z-[0.52em] backface-hidden">…

Each face is pushed half a line-height toward the viewer; the alternate face is pre-rotated onto the top of the prism. Rotating the core −90° brings the top face forward.

Make it yours

  • Direction: swap the signs for a slide-down; use translate-x for sideways.
  • Single-language: duplicate the same word in the twin and colour it with the accent for the classic agency effect.
  • Snappier prism: change the overshoot curve ease-[cubic-bezier(.68,-0.15,.27,1.15)] to ease-out.

Gotchas — read before shipping

  • v4 3D utilities compose via CSS variables: rotate-x-* and translate-z-* on the same element combine in the right order.
  • overflow-clip (not hidden) avoids creating a scroll container that would eat focus rings.
  • Keep the prism's aria-label on the link — both faces are aria-hidden.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

3D transforms are universal; backface-visibility is unprefixed in Safari 15.4+.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…