Video text plays a looping video through the shape of a word, using the letterforms as a window onto the footage underneath. Watch it below.
Video text plays a looping video through the shape of a word. The letters act as a window: only the pixels covered by the glyphs stay opaque, so the full-bleed video underneath shows only inside the letterforms. It fills its container and scales the type to the container width.
Reach for it when a single word carries a headline and you want texture or motion inside it: a hero title, a section marker, a loud one-word statement. The video does the work; the word gives it a mask.
Import the pattern from loosethreads:
import { VideoText } from "@pulp/loosethreads/patterns"Point src at a video and pass the word as children. Drive reducedMotion from the viewer's motion preference so the video is skipped when reduce is set:
import { VideoText } from "@pulp/loosethreads/patterns"
import { useEffect, useState } from "react"
function OceanHeadline() {
const [reducedMotion, setReducedMotion] = useState(false)
// Skip the video for viewers who ask for reduced motion.
useEffect(() => {
const query = window.matchMedia("(prefers-reduced-motion: reduce)")
const update = () => setReducedMotion(query.matches)
update()
query.addEventListener("change", update)
return () => query.removeEventListener("change", update)
}, [])
return (
<VideoText
src="https://cdn.magicui.design/ocean-small.webm"
reducedMotion={reducedMotion}
>
OCEAN
</VideoText>
)
}| Prop | Type | Default | Description |
|---|---|---|---|
src | string | Required | URL of the video revealed through the text. It plays muted, loop, and inline, and autoplays while on screen. |
children | ReactNode | Required | The word to reveal the video through. It is serialized into the SVG mask and also rendered as visually-hidden text for search and assistive tech. |
reducedMotion | boolean | Required | When true the video is not mounted at all and the word renders as a plain filled heading, so there is zero motion. Wire it to prefers-reduced-motion. |
An inline SVG <text> element is serialized to a data:image/svg+xml URL and applied as a CSS mask-image over a full-bleed <video>. A resize listener re-renders the mask so the vw-based font size tracks the container width. Alongside the masked layer sits a visually-hidden copy of the word for non-visual consumers.
The video pauses off-screen. A playing video keeps the decoder and compositor busy even when it is not visible, so the component pauses it while scrolled out of view and resumes on return, freeing the main thread for scrolling and input.
prefers-reduced-motion. Pass reducedMotion={true} when reduce is set: the video is not mounted and the word renders as a static filled heading.Next: Safari frame. Back to Text effects.