Text animation
The headline enters one word or one letter at a time. Screen readers read the full title one time.
Version 1.0
Build pages that feel alive
Build pages that feel alive
Reveal animations for React, Vue, Astro and plain JavaScript.
Your system reduces motion, so the preview does not animate. The code still follows your choices.
import { Reveal } from "@awesome-reveal/react";import { slide } from "@awesome-reveal/react/keyframes";import { Fragment } from "react";
export function Hero({ title }) { return ( <h1> <span className="visually-hidden">{title}</span> <Reveal as="span" className="words" keyframes={slide()} stagger={80} aria-hidden="true" > {title.split(" ").map((word, index) => ( <Fragment key={index}> {index > 0 && " "} <span>{word}</span> </Fragment> ))} </Reveal> </h1> );}<script setup>import { Reveal } from "@awesome-reveal/vue";import { slide } from "@awesome-reveal/vue/keyframes";
defineProps(["title"]);</script>
<template> <h1> <span class="visually-hidden">{{ title }}</span> <Reveal as="span" class="words" :keyframes="slide()" :stagger="80" aria-hidden="true" > <template v-for="(word, index) in title.split(' ')" :key="index"> {{ index > 0 ? " " : "" }}<span>{{ word }}</span> </template> </Reveal> </h1></template>---import { Reveal } from "@awesome-reveal/astro";import { slide } from "@awesome-reveal/astro/keyframes";
const { title } = Astro.props;---
<h1> <span class="visually-hidden">{title}</span> <Reveal as="span" class="words" keyframes={slide()} stagger={80} aria-hidden="true" > {title.split(" ").map((word, index) => ( <>{index > 0 && " "}<span>{word}</span></> ))} </Reveal></h1><h1> <span class="visually-hidden">Build pages that feel alive</span> <span class="words" aria-hidden="true">Build pages that feel alive</span></h1>import { reveal } from "@awesome-reveal/core";import { slide } from "@awesome-reveal/core/keyframes";
const words = document.querySelector("h1 .words");words.replaceChildren( ...words.textContent.split(" ").flatMap((word, index) => { const span = document.createElement("span"); span.textContent = word; return index > 0 ? [" ", span] : [span]; }),);
reveal(words, { keyframes: slide(), stagger: 80,});.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;}
/* Transforms do not apply to inline elements. */.words > span { display: inline-block;}Keep the text accessible
Section titled “Keep the text accessible”Split text is a problem for screen readers: they can read each word or letter as a separate item. The code prevents this in two steps:
- The full title is in an element that only screen readers see.
visually-hiddenis a class instyles.css. It hides the element visually, but keeps it for assistive technology. - The animated copy has
aria-hidden.
Then screen readers read the title one time, and find-in-page finds the full title.
- Choose
staggerbetween 20 and 40 ms for letters, and between 40 and 80 ms for words. A long text with a large value is slow to finish. - Use a short
durationfor letters, for example400. - Use
fade()or a smallslide({ distance: 12 }). Large movements of many small elements look noisy. - With
once: false, the text hides and plays again at each entry. See Viewport options.
Next steps
Section titled “Next steps”StaggerAnimate the children of an element one after the other.
KeyframesThe built-in animations, springs and custom keyframes.
AccessibilityReduced motion, screen readers and pages without JavaScript.
Getting startedInstall the package for your framework.
Awesome Reveal is free and open source. Sponsor it on GitHub.