Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Text

Pro

Outline Text for React

Hollow outlined text that floods with colour from a side, on hover, in view or in a loop. For React with Base UI, when a headline or a link should fill in as you reach it.

Framework

Outline Text draws a word as a hollow outline, then floods it with colour like ink rising through the letters: from the bottom, the top, the start, the end, or the middle outward. It can fill while the pointer is over it (or the link around it has keyboard focus), once when it scrolls into view, or over and over in a loop.

Use it for a big headline on a portfolio or an agency page, a case study's title, or a link that should answer the pointer. For text that's wiped into view from nothing, use Mask Reveal Text; for a printed shadow behind solid letters, Line Shadow Text. The outline is the real text, with a stroke and no fill; the fill is a copy on top, clipped away to its starting side and opened by a CSS clip-path transition (or a keyframe animation the install adds to your stylesheet, for the loop), with no animation library. With reduced motion the fill appears at once.

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 { OutlineText } from "@/components/ui/outline-text";
<h2>
  <OutlineText text="Hover me" />
</h2>

Demos

Fill from

fillFrom is where the colour comes in from: bottom by default, top, start or end (which follow the text's reading direction), or center, from the middle outward. Shown here with trigger="loop", which fills, holds, drains and rests, again and again.

Preview width

View

trigger="view" fills the text once, when half of it has scrolled into view, and leaves it filled. duration is how long the fill takes, 700 ms by default.

Preview width

Colors

strokeColor and fillColor take any CSS colours, including your theme's variables; both are the text's own colour by default, and strokeWidth is the outline's width in pixels, 1.5 by default. Inside a link, the fill also comes in when the link has keyboard focus.

Preview width

Accessibility

Screen readers read the text once: the outline is the real text, and the fill is a decorative copy they skip. In a link, keyboard focus fills it just as the pointer does. In forced-colors mode, where the outline's transparent fill would leave little to read, the text is drawn solid in the system's text colour. With reduced motion the fill appears and disappears at once, and the loop stays an outline.

FAQ

How is the outline drawn?

With -webkit-text-stroke, supported in every current browser, on the real text with its fill made transparent. The fill is a copy of the text laid exactly over it, clipped with clip-path: inset() so that only the part that has "risen" shows.

Why do I see lines inside some letters?

Some fonts, many variable fonts among them, build letters from overlapping shapes, and the stroke traces every shape, joins included. A static cut of the font, or another family, draws clean outlines; the effect is clearest at a bold weight and a large size.

Does the view trigger play again when I scroll back?

No. It fills once, the first time half the text is in view, and stays filled. Without JavaScript it shows filled.

What do start and end mean in a right-to-left language?

The start of the text's own reading direction: Arabic or Hebrew text fills from the right with start, and English text from the left, whatever the page's direction.

What happens with reduced motion?

The fill appears at once, without the wipe, and the loop doesn't run: the text stays an outline.

API Reference

OutlineText

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

PropType
textstring

The text.

duration?number

How long the fill takes to flood the letters, in milliseconds.

Default
700
fillColor?string

The fill's colour, any CSS colour. By default the text's colour.

fillFrom?"bottom" | "top" | "start" | "end" | "center"

Where the fill comes in from: a side (start and end follow the text's reading direction), or the middle outward.

Type
"bottom" | "top" | "start" | "end" | "center"
Default
"bottom"
strokeColor?string

The outline's colour, any CSS colour. By default the text's colour.

strokeWidth?number

The outline's width, in pixels. @default 1.5.

trigger?"hover" | "view" | "loop"

What fills it: the pointer over it (or keyboard focus on the link around it), the text scrolling into view (once), or a loop that fills and empties.

Type
"hover" | "view" | "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.

AttributeType
data-view?string

"in" when visible is true, otherwise "out".

CSS variableType
--outline-text-durationstring

How long the fill takes to flood the letters (700ms).

--outline-text-fillstring

The fill's colour (the text's colour by default).

--outline-text-fromstring
--outline-text-strokestring

The outline's colour (the text's colour by default).

--outline-text-stroke-widthstring

The outline's width (1.5px).

All 97 text effects