Skip to content

PlAnimateSplit

A line of text arriving one part at a time. The other effects tell themselves off across their children; a line of text has none, so this one makes them.

React
tsx
import { PlAnimateSplit } from 'plass-ui';

<PlAnimateSplit effect="slide" stagger={60}>
  One design language, two libraries
</PlAnimateSplit>;
dart
import 'package:plass_ui/plass_ui.dart';

const PlAnimateSplit(text: 'One design language, two libraries');

Props

PropTypeDefaultDescription
children * stringThe line. A string, and it has to be: the component cuts it up, and there is nothing to cut inside a <strong>
by'word' | 'character''word'What it is cut into. character is not safe in every script: Arabic stops joining and an emoji is cut into its pieces
effect'fade' | 'grow' | 'slide' | 'zoom' | 'rotate' | 'blink' | 'reveal''fade'Which of the entrances each part plays
durationsharednumber400How long one run takes, in milliseconds. A number, never a CSS string
delaysharednumber0How long before it starts, in milliseconds
easingsharedstringthe house curveThe easing curve, written the way CSS writes it
repeatsharednumber | 'infinite'1How many times it runs. 'infinite' rather than Infinity, because that word is what reaches CSS
alternatesharedbooleanfalseRuns every other pass backwards, so a repeat returns instead of jumping
pausedsharedbooleanfalseHolds the animation where it is
triggershared'mount' | 'visible' | 'hover' | 'manual''mount'What starts it: mount as soon as it is on the page, visible when it is scrolled into view, hover while the pointer or focus is on it, manual only when play says so
playsharedbooleanRuns 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 every time the element comes back into view
thresholdsharednumber0.2With trigger="visible", how much of the element has to be on screen before it counts as visible, from 0 to 1
timelineshared'auto' | 'view''auto'What advances the animation: the clock, or the reader's scroll position. view ignores duration, delay, repeat and trigger, and runs against range instead
rangesharedstring'entry 0% cover 45%'As CSS writes an animation-range. Only read when timeline is view
staggersharednumber0Milliseconds added to each child's delay. 0 plays the box itself; anything else moves the effect onto the children and takes it off the box
durationStepsharednumber0Milliseconds added to each child's duration. Negative is allowed; floored at 0
reversesharedbooleanfalseRuns the set from the last child to the first. Only the order turns round; each child still plays forwards
renderReactElement | (props, state) => ReactElementRenders something other than a <span> (<h2 />)
PropTypeDefaultDescription
text * StringThe line. A string, and it has to be: the component cuts it up, and there is nothing to cut inside a <strong>
byPlAnimateSplitByPlAnimateSplitBy.wordWhat it is cut into. character is not safe in every script: Arabic stops joining and an emoji is cut into its pieces
style · textAlignTextStyle? · TextAlign?The text style the line is drawn in, and how the parts align once they wrap
from · distance · fadePlassSide · double · boolbottom · 12 · trueThe entrance, spelled as a side, a distance and a fade. React names a CSS keyframe instead, because there an effect is a named thing and here every effect is built out of widgets
stagger · reverseDuration · bool40ms · falseHow long after one part the next one starts, and whether it starts from the end of the line
durationsharedDurationDuration(milliseconds: 400)How long one run takes, in milliseconds. A number, never a CSS string
delaysharedDurationDuration.zeroHow long before it starts, in milliseconds
curvesharedCurve?the house curveThe easing curve, written the way CSS writes it
repeatsharedint?1How many times it runs. null is what never stops: there is no 'infinite' to write, and -1 would be a sentinel a caller has to look up
alternatesharedboolfalseRuns every other pass backwards, so a repeat returns instead of jumping
pausedsharedboolfalseHolds the animation where it is
triggersharedPlassAnimateTriggerPlassAnimateTrigger.mountWhat starts it: mount as soon as it is on the page, visible when it is scrolled into view, hover while the pointer or focus is on it, manual only when play says so
playsharedboolfalseRuns it when trigger is manual. Each false → true starts it over
oncesharedbooltrueWith trigger="visible", whether it runs only the first time. Off, it runs again every time the element comes back into view
thresholdshareddouble0.2With trigger="visible", how much of the element has to be on screen before it counts as visible, from 0 to 1

by="character" is not safe in every script

The one thing to know before using it.

A character part breaks the shaping between letters. Arabic stops joining, Devanagari conjuncts come apart, and an emoji built out of several code points is cut into its pieces, so a line that was one word becomes a row of unrelated glyphs.

word has none of those problems, is the default, and is what a headline wants anyway: a word arriving is something a reader can follow, and a letter arriving is decoration.

Gaps and parts

Whitespace is left as whitespace and never given an entrance of its own, animating the space between two words is nothing arriving, and it does not take a step of the stagger with it either. The second word starts one step after the first, not two.

Each part is inline-block, because a transform does not apply to a non-replaced inline element: without it a slide would fade and never move.

Writing the entrance

effect picks one of the seven keyframes, and stagger, durationStep and reverse mean exactly what they mean on a PlAnimateFade around a list of <li>s. This component is the splitting and nothing more.

The entrance is a side, a distance and a fade, exactly as PlAnimateAppear spells it, which is the widget that already tells one entrance off across a set of children.

The React build names a CSS keyframe instead, and that difference is not an inconsistency: over there an effect is a named thing the stylesheet knows about, and here every effect is built out of widgets. A split takes the parameters the widget beside it takes.

Accessibility

  • A screen reader is told the line, once. The parts are hidden from the accessibility tree and the whole line sits beside them, which is what stops a split headline being read out one word, or one letter, at a time. That is the defect this pattern is known for everywhere it appears without it.
  • Text selection and copying still give you the line, gaps included.
  • Where a reader has asked for less motion nothing plays, and the line is simply there.

Released under the MIT License