Skip to content

AnimateHeadline

One line replacing the one above it, on a timer. Every line sits in the same grid cell, so the box is as tall as the longest of them from the first frame and never resizes as the reel turns.

tsx
import { AnimateHeadline } from 'neba';

<AnimateHeadline interval={2000}>
  <span>faster</span>
  <span>quieter</span>
  <span>yours</span>
</AnimateHeadline>;

Props

PropTypeDefaultDescription
intervalnumber2600How long each line is held, in milliseconds. Counted from the moment a line arrives, so it is reading time
indexnumberWhich line is showing. Pass it to drive the reel yourself
defaultIndexnumber0Where an uncontrolled reel starts
onIndexChange(index: number) => voidCalled with the line that has just come up
loopbooleantrueStarts again after the last line. Off, the reel stops on the last one
risenumber | string'100%'How far a line travels as it comes up or leaves. '100%' is one line's own height
durationsharednumber480How long one run takes, in milliseconds
delaysharednumber0How long before it starts, in milliseconds
easingstringThe easing curve, as CSS writes it. Defaults to the house curve
repeatsharednumber | 'infinite''infinite'How many times it runs
alternatesharedbooleanfalseRuns every other pass backwards, so a repeat returns instead of jumping
triggershared'mount' | 'visible' | 'hover' | 'manual''mount'What starts it. visible is on scrolling into view, hover is under the pointer (focus counts), manual is whatever play says
playbooleanRuns it when trigger is manual. Each false → true starts it over
oncesharedbooleantrueWith trigger="visible", whether it runs only the first time. Off, it runs again on every return
thresholdsharednumber0.2With trigger="visible", how much of the element has to be on screen, from 0 to 1
pausedsharedbooleanfalseHolds the animation where it is
childrenReactNodeThe lines, in the order they should be read

Every other <div> attribute passes through to the root. The settings shared by every Animate* are defined in prop conventions.

Examples

interval and duration

interval is how long a line is held, counted from the moment it arrives rather than from the start of the cycle, so raising duration, the length of the transition itself, does not quietly eat the reading time.

Controlled

Pass index and the reel stops turning on its own: it becomes a way of moving between lines that something else decides. A step in a form, a tab, a timer of your own. onIndexChange reports the line that has just come up.

loop and rise

loop off stops the reel on the last line and leaves it there. rise is how far a line travels as it comes up or leaves: '100%', the default, is one line's own height, and a smaller value is a nudge rather than a replacement.

tsx
<AnimateHeadline loop={false} rise="0.4rem">
  <span>Uploading…</span>
  <span>Processing…</span>
  <span>Done</span>
</AnimateHeadline>

Accessibility

  • Only the line that is showing is on the accessibility tree; the others are visibility: hidden, which keeps their space without putting them in the reading order.
  • Nothing is announced when the reel turns. This is deliberate (a live region cycling every two seconds is unusable), and it is why the component is for a set of phrases where any one of them would have done, not for content a reader has to see.
  • A reduced-motion preference drops the transition. The lines still change, they simply arrive rather than slide.

Released under the MIT License