Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Text

Pro

True Focus for React

A sentence read one word at a time, like a camera pulling focus: one word sharp, the rest blurred, a frame gliding to the next. For React with Base UI, when a short line should be read word by word.

Framework

True Focus reads a short line out one word at a time, the way a camera pulls focus: the word in focus is sharp, the others soft and dim, and four corner marks glide from one word to the next to frame it. Point at a word and it comes into focus and stays there; move away and the reading carries on from it.

Use it for a short slogan, a three-word promise or a hero line whose words each deserve a beat. To bring a whole line into focus once, as it arrives, use Blur Text; to change one word in place, Flip Words. A small timer moves the focus, and CSS transitions blur the words and slide the frame to where the word in focus sits, measured as it moves, with no animation library. With reduced motion every word is sharp.

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 { TrueFocus } from "@/components/ui/true-focus";
<h1>
  <TrueFocus text="Design Build Ship" />
</h1>

Demos

Intensity

intensity is how blurred the words out of focus are: subtle keeps them legible, medium (the default) softens them, and strong all but hides them, for a line of three or four words that are each worth the wait.

Preview width

Color

frameColor takes any CSS colour, such as var(--destructive); duration is how long each word stays in focus, 2000 ms by default. A long line wraps like any other text, and the frame follows the words onto the next line.

Preview width

Accessibility

Screen readers read the whole line once, from a visually hidden copy; the blurred words and the frame are hidden from them. The focus moves on its own, so nobody needs a pointer to read every word, and it holds still while the pointer is over the line. With reduced motion nothing moves and every word is sharp; without JavaScript the words are sharp too.

FAQ

How are the words split?

At the spaces. A word keeps the punctuation next to it ("Subtle," is one word), and the spaces between words stay as they are, so the line wraps where it would anyway.

What does the pointer do?

Over a word, it brings that word into focus at once and holds it there; between words, it only holds the focus where it is. When the pointer leaves, the focus moves on after duration.

How does the frame find the word?

Each time the focus moves, the word's place and size are read once from the page and written to four CSS variables; the frame's corners slide there with a CSS transition. Nothing runs between moves.

Can every word still be read?

Yes, in turn: every word comes into focus as the line is read, and subtle keeps the others legible meanwhile. Keep the line short, three to six words, so nobody waits long for the one they want.

How do I change how fast the frame moves?

Set --true-focus-move on the component, such as className="[--true-focus-move:300ms]": it's the time the frame takes to glide and the words take to blur, 500 ms by default.

What happens with reduced motion?

Every word is sharp, the frame is hidden and the focus doesn't move: the line reads as plain text.

API Reference

TrueFocus

<span> data-slot="true-focus"

PropType
textstring

The text; each word takes its turn in focus.

duration?number

How long each word stays in focus, in milliseconds. @default 2000.

Default
2000
frameColor?string

The frame's colour, any CSS colour. @default "var(--primary)".

intensity?"subtle" | "medium" | "strong"

How blurred the words out of focus are. @default "medium".

Type
"subtle" | "medium" | "strong"
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.

AttributeType
data-active?string
data-index?string

Mirrors word.

CSS variableType
--true-focus-blurstring

The blur out of focus (0.07em).

--true-focus-colorstring

The frame's colour.

--true-focus-movestring

How long the focus takes to move (500ms).

All 97 text effects