Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Text

Pro

Caps Toggle Text for React

Text that turns to capitals and back, letter by letter, in a wave along the line. For React with Base UI, when a label or a link should answer the pointer with a change of case.

Framework

Caps Toggle Text turns a line to capitals and back without moving it: each letter cross-fades to its other case a moment after the one before it, the leaving letter shrinking and sinking as its twin grows in, so a wave of capitals runs along the line. It turns while the pointer is over it, or on its own, in a loop.

Use it for navigation links, a button label or a wordmark that should react to the pointer with a typographic touch. For the quieter change to the font's small capitals, use Smallcaps Text; for words that change altogether, Morph Text. The text and its capitals sit in the same place and CSS transitions turn each letter, or CSS keyframe animations the install adds to your stylesheet for the loop, with no animation library and no script. With reduced motion the case changes at once and the loop rests.

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 { CapsToggleText } from "@/components/ui/caps-toggle-text";
<a href="/work">
  <CapsToggleText text="work" />
</a>

Demos

Hover

trigger="hover", the default, turns the text while the pointer is over it, and back when it leaves. Inside a link or a button it also turns while that has keyboard focus, so the keyboard sees what the pointer sees.

Preview width

Timing

With trigger="loop", duration is how long each case stays, 2400 ms by default. stagger is the time between one letter turning and the next, 35 ms: short for a snap, long for a slow wave.

Preview width

Accessibility

Screen readers read the text once, as written, from a visually hidden copy; the two layers of letters are hidden from them. The loop pauses while the pointer is over it, and with reduced motion the case changes without fading or scaling, and the loop stays on the text as written.

FAQ

What case should I write the text in?

The case it should rest in. It shows as written, and turns to capitals: write hello world for a lowercase line that turns to HELLO WORLD, or Work for a link that keeps its capital at rest. Text already in capitals has nothing to turn.

Does the line change width when it turns?

No. The text and its capitals are laid out in the same place, so the wider capitals hold the space from the start and nothing around the text moves. The text as written sits at the start of that space, or in its middle in centred text.

Can it turn on click?

Not by itself: a change of case isn't an action, and text that reacts to a click no keyboard can reach leaves keyboard users out. Put it inside a real button or link instead, and it turns while that is hovered or has keyboard focus.

Does it work with any font?

Yes. Both layers use the same font, weight and spacing; only the case differs, through CSS text-transform. Letters whose capitals differ only in size (o, s, x) simply grow.

What do screen readers hear?

The text once, as written. Capitals are only a look here, so they aren't read out letter by letter.

What happens with reduced motion?

On hover the case still changes, at once, without fading or scaling. The loop rests on the text as written.

API Reference

CapsToggleText

<span> data-slot="caps-toggle-text"

PropType
textstring

The text, as written: it shows like this at rest, and in capitals when it turns.

duration?number

With trigger="loop", how long each case stays, in milliseconds. @default 2400.

stagger?number

The time between one letter turning and the next, in milliseconds. @default 35.

trigger?"hover" | "loop"

What turns it: the pointer over it (or keyboard focus on the link or button around it), or a loop on its own.

Type
"hover" | "loop"
Default
"hover"
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

Merged with the part's classes.

CSS variableType
--caps-toggle-text-durationstring

With trigger="loop", how long each case stays (2400ms).

--caps-toggle-text-staggerstring

The time between one letter turning and the next (35ms).

All 97 text effects