Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Text

Pro

Pendulum Text for React

Letters that hang on threads and swing like pendulums when the pointer brushes or presses them. For React with Base UI, when a headline should sway at a touch.

Framework

Pendulum Text hangs each letter from a thread. Brush a letter with the pointer and it swings away from it; press and every letter swings away from the press, the nearest hardest. Each one sways back and forth, smaller every time, until it hangs still again, and since no two threads are quite the same length, letters swung together slowly drift out of step. At rest each letter hangs at a slight tilt of its own.

Use it for a playful headline, a shop sign, a toy or a 404 page. For letters that wobble on their own, use Wobble Text; for a slow drift that never stops, Drift Text. A swing is a CSS keyframe animation the install adds to your stylesheet, a damped turn about a point above the letter; a handler writes how hard and which way to a CSS variable and restarts it, with no state, no animation library and no frame loop. With reduced motion the letters hang still.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Usage

import { PendulumText } from "@/components/ui/pendulum-text";
<h1>
  <PendulumText text="Swing" />
</h1>

Demos

Angle

angle is the widest a letter swings either way, 20° by default, and duration how long a swing takes to die down, 2400 ms. A gentle sway wants a small angle and a short swing; a wild one, both larger.

Preview width

Strings

The threads are faint lines in the text's own colour. strings={false} hides them; the letters still hang from the same point above them.

Preview width

Accessibility

Screen readers read the text once, from a visually hidden copy, never letter by letter. The swing is decoration: it answers the pointer only, has no stop in the tab order and changes nothing, so keyboard and screen reader users miss nothing. The letters stay readable as they sway, and with reduced motion they hang still, each at its slight tilt.

FAQ

Is it a physics simulation?

No. Every swing is the same damped keyframe animation: a quick kick out, then sways through the rest position, each about two thirds of the last and just as long, the way a real pendulum keeps its beat as it dies down. What differs from letter to letter is how hard it's pushed, which way, and its beat, a tenth longer or shorter than the next one's, all CSS variables.

Why doesn't a swinging letter answer the pointer?

A letter that's already swinging ignores a brush until it's still, so a pointer resting at its edge can't keep it going forever. A press swings every letter again, from wherever it is.

Why do neighbouring letters overlap mid-swing?

At wide angles neighbours swing into each other's space for a moment, like charms on a bracelet. Keep angle under about 25° for tightly set text, or give it some letter spacing.

Can I style the threads?

Each thread is its letter's ::before, a 1px line in the text's colour at a quarter strength, so it swings with the letter. Style [data-slot=pendulum-text-char]::before to change it, or pass strings={false} to hide it.

What happens on a touch screen?

A tap swings every letter away from it. Brushing needs a pointer that can hover, such as a mouse or a pen.

What happens with reduced motion?

The letters hang still at their slight tilts, and neither a brush nor a press swings them.

API Reference

PendulumText

<span> data-slot="pendulum-text"

PropType
textstring

The text.

angle?number

The widest a letter swings either way, in degrees. @default 20.

duration?number

How long a swing takes to die down, in milliseconds. @default 2400.

strings?boolean

A faint thread above each letter, the string it hangs from. @default true.

Default
true
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Forwarded to the part.

CSS variableType
--pendulum-text-anglestring

The widest a letter swings either way (20deg).

--pendulum-text-durationstring

How long a swing takes to die down (2400ms).

  • Pro

    Letters that rock on their own

  • Pro

    Elastic Text

    Interactive

    Letters stretch, then spring back

  • Pro

    Letters that fall and bounce

  • Pro

    Ripple Text

    Interactive

    A wave from where you click

  • Pro

    Shatter Text

    Interactive

    Letters that burst apart on a click

  • Turn it over

    Pro

    Tilt Text

    Interactive

    The word tilts in 3D to the pointer

All 97 text effects