AnimateTyping
Text appearing one character at a time. The whole string is in the document from the first frame for a screen reader, and what animates is a copy that is hidden from one.
import { AnimateTyping } from 'neba';
<AnimateTyping speed={18}>Ship the interface, not the design system.</AnimateTyping>;Props
| Prop | Type | Default | Description |
|---|---|---|---|
| text | string | — | The text, when it is easier to pass than to nest. Overrides children |
| speed | number | 24 | How fast it is typed, in characters per second |
| hold | number | 1400 | How long the finished text is held before it repeats, in milliseconds |
| erase | boolean | false | Deletes the text again before repeating, rather than clearing it in one frame |
| eraseSpeed | number | — | How fast it is deleted, in characters per second. Defaults to twice speed |
| caret | boolean | true | The block after the text |
| caretChar | ReactNode | '|' | What the caret is drawn as |
| durationshared | number | — | How long one run takes, in milliseconds |
| delayshared | number | 0 | How long before it starts, in milliseconds |
| easing | string | — | The easing curve, as CSS writes it. Defaults to the house curve |
| repeatshared | number | 'infinite' | 1 | How many times it runs |
| alternateshared | boolean | false | Runs 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 |
| play | boolean | — | Runs it when trigger is manual. Each false → true starts it over |
| onceshared | boolean | true | With trigger="visible", whether it runs only the first time. Off, it runs again on every return |
| thresholdshared | number | 0.2 | With trigger="visible", how much of the element has to be on screen, from 0 to 1 |
| pausedshared | boolean | false | Holds the animation where it is |
| children | ReactNode | — | The text to type. Only text is typed: an element among the children contributes its text and nothing about its markup |
Every other <div> attribute passes through to the root. The settings shared by every Animate* are defined in prop conventions.
Only text is typed. Pass a string, or strings; an element among the children contributes its text and nothing about its markup, because there is no honest way to reveal half of a link. text is the same thing as a prop, and wins when both are given.
Examples
speed
Characters per second. A speed rather than a duration, because a long line and a short one should be typed at the same pace rather than in the same time: though duration is still accepted, and means the time for the whole string.
repeat, hold and erase
repeat is how many passes it makes and hold is how long the finished text stays up between them. Without erase a repeat clears in one frame, which is right for a line being replaced; with it the text is deleted a character at a time, at eraseSpeed: twice speed by default, which is what a person actually does.
delay
Milliseconds before it starts. A delay per line is what turns a stack of typewriters into a transcript.
caret
The block after the text, on by default. caretChar is what it is drawn as: ▌, _, anything.
<AnimateTyping caretChar="▌" caret={false}>
No caret at all
</AnimateTyping>Accessibility
- The full text is in the document from the first frame, in a clipped box, and the animated copy is
aria-hidden. A screen reader reads the line once and is not made to sit through the performance. - A reduced-motion preference shows the whole string immediately, with no typing at all.
- The box is not laid out from the characters that have arrived, so the text around it does not reflow on every frame.
- Characters are counted as graphemes, not code points:
한and👩👩👧each arrive in one step rather than in three or seven.