3D✨ Premium

Rotating Text

A word in an indigo → magenta gradient, with no box around it, gently sways in 3D (±15° around the vertical axis, ±10° around the horizontal one, 4 s loop) and spins a full turn in 1 s when you hover it or it gets keyboard focus. Pure CSS, zero JavaScript, front-facing and still when the user reduces motion.

CSS3DText

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. 3D has 50 effects, including 8 free. Explore the category →

3 usage examples

Hanging sign swaying in the wind — website of a hikers' lodge in Auvergne — Rotating Text example 1

① Hanging sign swaying in the wind — website of a hikers' lodge in Auvergne

WhenLodge banner at dusk: the name "AU BON GÎTE" hangs under an iron bracket fixed to the wall and sways like a sign; hovering the name spins it around as if caught by a gust of wind.
WhyThe slow two-axis sway is that of a hanging sign; text rippling letter by letter would look like a flag, not a sign.
SettingsGeorgia 46 px, 0.04 em letter spacing; golden #fde68a → #f59e0b → #b45309 gradient set as background-image (the text clipping comes from the sold code) plus a drop-shadow; bracket, chains and hills drawn in fixed CSS around it.
The boat's name rolling with the swell — booking page for a sea trip — Rotating Text example 2

② The boat's name rolling with the swell — booking page for a sea trip

WhenOn the sailboat's blue hull, the name "L'ALBATROS" painted in a gradient rocks to the loop's rhythm; below, "Departs 9 am, back 12 pm, 18 seats" and the booking button.
WhyA single word swaying in 3D is enough to suggest the swell, with no wave illustration.
Settings44 px, 0.06 em letter spacing; white → sky blue #ffffff → #bae6fd → #38bdf8 gradient; hull, mast and sail in fixed CSS, sea in a gradient; no change in duration.
The winning word spins around — results of a neighborhood association's dictation contest — Rotating Text example 3

③ The winning word spins around — results of a neighborhood association's dictation contest

When"Neighborhood dictation 2027" page: the year's trick word "CHRYSANTHÈME" sways at the center, above the podium; on hover it spins a full turn.
WhyThe full turn on hover works as a small celebration on the word itself, with no added confetti or banner.
Settings30 px over a lined-notebook background; original gradient. Since hovering the word is too small a target for the capture's mouse, a scene script replays the same turn (textSpin) every 2 s until the first real hover or focus, and does nothing under reduced motion.

How it works

The .fx-0008-root root (#0a0a0f background, at least 300 px tall) centers .text-rotate-container, which carries the camera (perspective: 400px, short, so that 15° shows on a single word) and tabindex="0". Inside it, .text-rotate (28 px, weight 800, transform-style: preserve-3d) plays textRotate3D: 4 s, ease-in-out, forever; at 0, 50 and 100% the word faces front, at 25% it turns 15° to the right and tips 10° backward, at 75% the opposite. The result is a slow figure-eight sway, like a hanging sign. The text itself is an inline-block <span> whose background is a 135° #6366f1 → #d946ef gradient, clipped to the letter shapes (background-clip: text, plus its -webkit- version) and revealed by -webkit-text-fill-color: transparent.

The full turn is a second animation, textSpin (rotateY(0) → rotateY(360deg), 1 s, once), which replaces the sway when .text-rotate-container is hovered or has keyboard focus (:focus-visible, 2 px #a5b4fc outline). Once the turn ends, the word stays front-facing while the pointer is on it; it sways again when the pointer leaves. Hover is read on the container, which does not turn, not on the word: seen edge-on mid-turn, the word has almost no surface left, and a hover read on it would get lost. Measured in Chromium 151 and Firefox 153, with the pointer near the edge of the word moving by 3 px: on the container, the turn goes all the way; a hover read on the word itself restarts the turn from zero about every 0.35 s.

The sway only moves one word: 0.44% of the block's pixels change over 0.8 s, so the effect stays subtle. Only transform is animated. Two copies on one page sway and spin independently. The text-clipped gradient does not depend on 3D; the rotation does: Playwright's WebKit flattens all CSS 3D, so rendering on a real Safari was not verified.

Accessibility

  • Reduced motion: under prefers-reduced-motion: reduce, a block scoped to .fx-0008-root removes the sway and the turn: the word stays front-facing. On hover and focus, it brightens (filter: brightness(1.35)) instead of turning. Measured in Chromium and Firefox: 0 running animations, 0% of pixels changing over 0.8 s, no animation on hover.
  • Readability: it is real text, read as-is by screen readers and selectable. The gradient runs from 4.4:1 (indigo) to 5.7:1 (magenta) on the #0a0a0f background, above the 3:1 threshold for 28 px bold text. During the turn the word is unreadable for half a second: do not use it for information people need to read at hover time.
  • Keyboard: the container has tabindex="0", and the turn starts on keyboard focus with a visible outline. If the word is a link, remove that tabindex and target the link instead (a:focus-visible .text-rotate): a focusable element that does nothing adds a useless stop to keyboard navigation.
  • Touch: on a phone, tapping the word triggers the browser's emulated hover, hence one turn; the sway runs with no gesture at all.

Browser compatibility

CSS (3D transform, perspective, background-clip: text, -webkit-text-fill-color, @keyframes), zero JavaScript, no dependencies. Measured in Chromium 151 and Firefox 153: same sway (0.68% difference on the still frame, due to text antialiasing), full turn on hover and focus, two independent copies, 0 errors. Playwright's WebKit 26.5 flattens all CSS 3D: the real Safari was not verified.

Chrome 36+✓ Full (measured)
Firefox 49+✓ Full (measured)
Safari✓ Not verified on a real Safari
Edge 79+✓ Full (Chromium engine)
Mobile iOS✓ Not verified on a real Safari
Android Chrome✓ Full (Chromium engine, turns on tap)

Without CSS 3D, the word stays flat and the sway shrinks to a slight horizontal stretch. Without background-clip: text, -webkit-text-fill-color is usually ignored as well and the word shows in the text color (white), without the gradient.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0008-root">
  <div class="demo-3d">
    <div class="text-rotate-container" tabindex="0">
      <div class="text-rotate">
        <span>3D TEXT</span>
      </div>
    </div>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Sway amplitude (CSS — @keyframes textRotate3D, 25% and 75% steps) rotateY ±15°, rotateX ±10° ±8° / ±5° for a word that barely breathes; ±25° / ±15° for a sign battered by the wind. Keep 0% = 50% = 100% front-facing: the loop goes through rest twice.
Sway duration (CSS — 4s in the animation of .text-rotate) 4 s, ease-in-out 6 s for a heavy sign, 2.5 s for a livelier effect. ease-in-out slows down at the ends, like a hanging object.
Perspective (CSS — perspective on .text-rotate-container) 400px Kept short, it makes small angles visible. 800 px flattens the sway; 250 px makes it very pronounced, at the risk of distorting long words.
Size and font (CSS — font-size and font-weight of .text-rotate) 28 px, 800 A 40 to 50 px word shows the 3D better. Bold letters keep enough surface for the gradient to read.
Gradient (CSS — background of .text-rotate span) linear-gradient(135deg, #6366f1, #d946ef) Change only the image (background-image) to keep the text clipping. Check the contrast of the darkest color against your background.
Turn on hover (CSS — 1s and textSpin in the :hover, :focus-visible rule) one turn in 1 s, once 1.6s for a more solemn turn. For two turns, write rotateY(720deg) in textSpin. Delete the rule to keep only the sway.

FAQ

Because the word turns: at 90°, seen edge-on, it has almost no surface left under the pointer. A hover read on the word gets lost at that moment, the animation goes back to swaying, the word faces front again, the hover comes back… and the turn restarts from zero without ever finishing (measured: about every 0.35 s with the pointer near the edge). The container does not turn: its surface stays the same throughout the turn.
Yes: put an <a href="…"> in place of the <span> or around the container, remove tabindex="0" and add a:focus-visible .text-rotate to the turn rule. Keep the target large enough: 28 px tall is a minimum for a finger.
The code declares the clipping twice, -webkit-background-clip: text then background-clip: text, which covers browsers that only know one of the two forms. In the rare case where neither works, -webkit-text-fill-color is usually ignored too and the word shows in white, readable, without the gradient.