Text Animations

Strike Reframe Text

A strike-and-reframe text React component: a sentence is struck through with a hand-drawn line, then a new sentence rises from below to replace it. Chainable for objection-to-answer and old-claim-to-new-claim reframes.

A text animation that strikes out the old claim and swaps in the new one. Each pair shows its sentence in full, then a slightly wobbly hand-drawn line sweeps across from left to right, the old sentence fades out of the way, and the new one springs up from below with a soft blur — optionally with a highlight brushed in behind it from left to right. Multiple pairs play in sequence, which suits landing-page pain-point flips, product update announcements or slide-style before/after arguments. Turn on keepStruck to keep the struck sentence above as a small strikethrough note.

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add strike-reframe-text.en

New project or installation trouble? Start here →

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

450
1600
Active edited usageDefault props
<StrikeReframeText/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

Installation

npx webberui@latest add https://webberui.com/r/strike-reframe-text.en.json

Or, once registries are configured in components.json, install it as @webberui/strike-reframe-text.

Usage

import { StrikeReframeText } from "@/components/ui/strike-reframe-text";

<StrikeReframeText
  as="h2"
  loop
  pairs={[
    { from: "Reports eat a whole afternoon", to: "Reports generate in three minutes" },
    { from: "Reconciling means copy-pasting every time", to: "Statements reconcile themselves" },
  ]}
  className="text-3xl font-semibold"
/>

Props

PropTypeDefaultDescription
pairsStrikeReframePair[]—Pairs played in order; each strikes out from, then to rises to replace it
as"p" | "h2" | "span""p"Root element tag; span renders inline-block so it can sit inside a paragraph
autoPlaybooleantrueAdvance to the next pair automatically; when off, only the current pair plays
trigger"mount" | "inView""inView"When playback starts: on mount, or once scrolled into view (fires once)
loopbooleanfalseReturn to the first pair after the last one finishes
keepStruckbooleanfalseKeep the struck state: the old sentence shrinks and moves above as a strikethrough note
highlightbooleantrueBrush a highlight in from left to right behind the new sentence
strikeDurationnumber450Strike duration (ms); when omitted reads --wb-strike-reframe-duration, then falls back to 450
holdBeforenumber900How long the old sentence is shown before the strike starts (ms)
holdAfternumber1600How long the new sentence stays before moving to the next pair (ms)
indexnumber—Controlled current pair index; when provided the parent switches pairs and the component only animates
defaultIndexnumber0Initial pair index in uncontrolled mode
onIndexChange(index: number) => void—Called when the component wants to advance; in controlled mode you decide whether to accept it
classNamestring—Passed to the root element; set font size, weight and colour here

StrikeReframePair

FieldTypeDefaultDescription
fromstring—The old sentence shown first, then struck through
tostring—The new sentence that rises from below to replace it

How it works

  • Per-pair timing: old sentence holds for holdBefore → strike over strikeDuration (easing [0.65, 0, 0.35, 1]) → the new sentence springs up (stiffness 320, damping 28) from 18px below with a 4px blur → holds for holdAfter, then the next pair starts.
  • The strike is measured from the old sentence's actual layout (ResizeObserver, plus a re-measure once web fonts load). Wrapped sentences get one stroke per line, drawn in sequence. The wobble is derived from a hash of the sentence, so the same text always gets the same line and server and client output match.
  • Override the look with CSS variables: --wb-strike-reframe-line (line colour, default #f43f5e), --wb-strike-reframe-highlight (highlight colour, default rgb(250 204 21 / 0.35)) and --wb-strike-reframe-duration (strike duration, default 450ms).
  • Old and new sentences share one grid cell, so the container takes the larger of the two and the layout does not jump when they swap; with keepStruck the root reserves space above for the shrunken strikethrough note.
  • In controlled mode the component only proposes the next pair through onIndexChange once a pair finishes; if you do not update index, it stays on the current pair.

Accessibility

  • The animated visual layer is aria-hidden; screen readers get sr-only text reading "Before: old sentence; now: new sentence", so the meaning never depends on the animation.
  • A separate aria-live="polite" region announces the new sentence only when the pair changes; nothing is announced on first load, to avoid repetition.
  • With as="h2" the heading's accessible name is the full before/now sentence, not just the struck half.
  • When the user has enabled reduce motion in their OS, no line is drawn and nothing travels: the component cross-fades straight to the new sentence in 150ms and keeps the old one above as small strikethrough text.