Back

Enhancing UI with Staggered Text Animations

Enhancing UI with Staggered Text Animations

User interfaces are, at the core, a presentational unit of web applications as they communicate a web application’s purpose to users, interact with users, and convey site functionalities. As such, there is a drive to make user interfaces simple, intuitive, and functional to drive user conversion rates. Staggered text animations provide extra interest to your page, as this article will show.

Staggered text animations present a method of arranging texts on a web application to best convey the site’s key purpose and features and provide a structure for subheading content. Guiding the user observation means not overwhelming users with a lot of content simultaneously but rather presenting text in sequences in a carefully choreographed manner. This intentional delay, or stagger, in presenting UI elements can significantly impact the user’s perception and interaction. In this article, we’ll delve into the fascinating world of staggered animations, exploring how they enhance the overall user experience and contribute to creating more visually appealing and user-friendly interfaces.

What are staggered animations?

The term staggered animation refers to a technique used in web design and multimedia presentations. In this technique, visual changes to UI elements happen in series, sometimes overlapping, to enhance design capabilities, add a feel of interactivity, and captivate the site audience.

The purpose of staggered animation is to create a dynamic showcase of content in which the user is entranced, and their attention is guided to specific pieces of information, creating a sense of movement or progression that is visually appealing.

With staggered animations on individual elements, words, lines of text, or characters are made possible, and a delay is introduced between the appearance of each element, thereby creating a sequential or otherwise staggered presentation.

Let’s take a look at examples of some staggered animations:

"Elva Design Group"

In the GIF above, the site Elva Design Group uses staggered animations to display content on their page. Here the texts, and shapes are animated in a defined order, adding an aesthetic feel to the site.

Staggered animations can also be assigned to specific site sections, such as side menus, as illustrated in the GIF below:

Timothy Ricks

What is a staggered text animation?

Staggered text animation refers to a form of staggered animation applied to text elements, which involves an intentional delay between the occurrence of individual characters, words, or lines of text. In this animation, a technique is used to animate text elements sequentially orchestrated, creating dynamic and engaging visual effects and resulting in a gradual or progressive appearance of the text.

Applications and benefits of staggered animations

This section will discuss the benefits of using staggered animations on your web application and areas where its usage is more pronounced.

  • More pronounced Call-to-Action (CTA) elements: Staggered animations can draw attention to important elements such as call-to-action buttons or links on a web application, encouraging users to take action and providing better conversions. With staggered animations, these site elements are more easily noticed when they come into the user’s viewport than non-animated CTA elements.
  • Intuitive interfaces: Staggered animation can be applied as a core part of user interfaces to layer site content and provide digestible bits of information at their own pace and in manageable chunks.
  • User onboarding and tutorials: The user onboarding process and tutorial guide are best presented in measured bits containing simple animations to avoid overwhelming the user with too much information. Instead, an onboarding guide should introduce users step-by-step to key features and functionalities of the web application.
  • Data visualization and infographics: These animations are particularly beneficial in the field of data visualization as they can be used to animate data points or chart elements sequentially, helping users focus on specific insights or trends. For instance, in bar charts and line graphs, with the bars appearing in sequence one after the other, the user can be guided along the course of the presentation, getting the desired information from the start of the data analysis to the current state, making complex information more accessible and engaging.
  • Visual feedback and micro-interactions: SSite interactivity and visual feedback in response to user actions can be performed with staggered animations and provide an intuitive and responsive web application. Micro-interactions such as user clicks, swiping, tapping, or dragging are made more pronounced when an animation occurs to inform users of the successful completion or error occurrence based on their application and have been statistically proven to provide a positive user experience, a more user-friendly and engaging software product, and encourage users to revisit the application. A more profound use case of this application is in typing games, where visual cues are provided by the interface on what keys to press and feedback on key-down events.
  • Brand identity and aesthetics: A site with staggered animation can instill a highly immersive and enjoyable experience in users. Such sites employ a unique presentation approach, embedding themselves in the user’s memory and leveraging their satisfaction and cognitive remembrance to drive their usage and recommendations to a broader audience.
  • Site accessibility refers to the methods used to ensure a web application considers the diverse needs of a wide audience. Implementing staggered animations, such as staggered texts, can help enhance readability, retain user attention, and provide visual cues and paced content, making it easy for users of different backgrounds.
  • Navigation and Menus: Staggered animations can serve as visual cues or signifiers to convey meaning and context within the interface. For example, animating the expansion of a drop-down menu or the collapse of an accordion panel provides visual feedback that communicates the action’s outcome.

Setting up dev dependencies

In this section, we will set up Framer Motion in an existing web application project. For this installation, I will be making use of Node.js and a command-line interface in my project directory:

# bash commands
npm install framer-motion

Creating staggered text animations

In this section, we will create staggered text animations in a web application using Framer Motions.

Animating text characters

Here, we will apply staggered animation to individual text characters, applying a delay and moving these characters a little at a time.

"use client";
import React from "react";
import { motion } from "framer-motion";
const StaggeredAnimations = () => {
  // text to animate
  const firstText = "Welcome to Storytelling";

  //   to easily reuse the animation props
  const AnimationProps = {
    initial: { opacity: 0, y: 0 },
    animate: { opacity: 1, y: 20 },
 };
  return (
    <div>
      {/* staggered text animations */}
      {/* for screen readers */}
      <span className="sr-only">{firstText}</span>
      {/* for animation purposes and users */}
      {/* use the initial and animate states from AnimateProps */}
      <motion.span
        arial-hidden="true"
        className="text-8xl font-bold text-center"
        initial="initial"
        animate="animate"
        transition={{ staggerChildren: 0.1 }}
      >
        {/* render words */}
        {firstText.split(" ").map((word, index) => (
          <React.Fragment key={index}>
            {/* split and animate individual text  characters */}
            {word.split("").map((letter, index) => (
              <motion.span
                className="inline-block"
                key={index}
                variants={AnimationProps}
              >
                {letter}
              </motion.span>
 ))}
            {/* add space between words */}
            <motion.span
              className="inline-block"
              key={index}
              variants={AnimationProps}
            >
              &nbsp;
            </motion.span>
          </React.Fragment>
 ))}
      </motion.span>
    </div>
 );
};
export default StaggeredAnimations;

In the code block above, we created a variable firstText with the sentence “Welcome to Storytelling”. In the code above, we will apply a stagger animation which moves every character along the y-axis. The animation uses AnimationProps as a property to specify the initial and final values of the animation. The initial value object specifies that the element starts from a value 0 of opacity and y, to a final value of 1 for opacity and 20 on the y-axis.

In our component, we first created a span element meant for screen readers for accessibility purposes so that they only focus on the entire text with the class sr-only, as the other span with the stagger animation will cause a problem with screen readers. For the animation, we created another span element and added a property arial-hidden as true, to hide it from screen readers.

We used the motion component from Framer Motion and set an initial and animate state for the animation referencing the specified animation behavior in AnimationProps. We created two loops, with the first loop splitting the sentence variable firstText into words and the second loop splitting each word into individual characters. Finally, we set the variants property to use the defined animation properties and added a 0.1s delay between each animated child of the motion.span element, using transition={{ staggerChildren: 0.1 }}. Below is the output of this animation:

Triggering animations on page scroll

By default, animations occur on page load, regardless of whether the elements are in the viewport. To correct this, we will add a ref property to the element and only execute animations when the element is scrolled into view on the browser.

"use client";
import React, { useRef } from "react";
import { motion, useInView } from "framer-motion";
const StaggeredAnimations = () => {
  // text to animate
  const firstText = "Welcome to Storytelling";
  const secondText = "We will love to tell your story";
  //   to easily reuse the animation props
  const AnimationProps = {
    initial: { opacity: 0, y: 0 },
    animate: { opacity: 1, y: 20 },
 };
  const ref = useRef(null);
  const viewPort = useInView(ref, {
    amount: 0.5,
 });
  return (
    <div>
      {/* staggered text animations */}
      {/* for screen readers */}
      {/* First text animation */}
 //...
      {/* second text */}
      <div className="h-screen">
        <span className="sr-only">{secondText}</span>
        <motion.span
          ref={ref}
          arial-hidden="true"
          className="text-8xl font-bold text-center"
          initial="initial"
          animate={viewPort ? "animate" : "initial"}
          transition={{ staggerChildren: 0.1 }}
        >
          {secondText.split(" ").map((word, index) => (
            <React.Fragment key={index}>
              {word.split("").map((letter, index) => (
                <motion.span
                  className="inline-block"
                  key={index}
                  variants={AnimationProps}
                >
                  {letter}
                </motion.span>
 ))}
              <motion.span
                className="inline-block"
                key={index}
                variants={AnimationProps}
              >
                &nbsp;
              </motion.span>
            </React.Fragment>
 ))}
        </motion.span>
      </div>
    </div>
 );
};
export default StaggeredAnimations;

In the code above, we import useRef from React, and useInView from Framer. We also have the variables firstText and secondText each containing strings. We created a variable viewPort which uses the Framer useInView hook and a ref to determine if half of the element (the specified amount of 0.5 stands for half of the element) has entered the viewport. Using the boolean state of this variable, we set the animate property of the motion component, as such the animation only starts when the element comes into view.

Both animations performed on the secondText and firstText values make use of AnimationProps and change the values of opacity and y, the same as the previous section. Compared to the first animation, the second one only starts when the element is in the viewport due to the referenced ref value and the useInView hook in viewPort which we use as a condition for the animate property. If this value is false, it returns initial which returns the animation to the start values of the animation. When true, the animation is set to the animate property and the animation occurs.

Staggering multiple sentences

In this section, we will apply stagger animations to different sentences in the same section and set their delay property so they appear in sequence rather than all at once within the viewport.

// new sentence variable
const sitedescription = "Storytelling is the social and cultural activity of sharing stories, often with improvisation, theatrics, or embellishment. Stories or narratives have been shared in every culture as a means of entertainment, education, cultural preservation, and instilling moral values. Crucial elements of stories and storytelling include plot, characters, and narrative point of view.";
// animate property and delay for description
const AnimationDescription = {
  initial: { opacity: 0, y: 20 },
  animate: {
    opacity: 1,
    y: 0,
    transition: {
      delay: 3,
      type: "spring",
      stiffness: 120,
 },
 },
};
//...
<div className="h-screen">
  <span className="sr-only">{firstText}</span>
  {/* for animation purposes and users */}
  {/* use the initial and animate states from AnimateProps */}
  <motion.span
    arial-hidden="true"
    className="text-8xl font-bold text-center"
    initial="initial"
    animate="animate"
    transition={{ staggerChildren: 0.1 }}
  >
    {/* render words */}
    {firstText.split(" ").map((word, index) => (
      <React.Fragment key={index}>
        {/* split and animate individual text  characters */}
        {word.split("").map((letter, index) => (
          <motion.span
            className="inline-block"
            key={index}
            variants={AnimationProps}
          >
            {letter}
          </motion.span>
 ))}
        {/* add space between words */}
        <motion.span
          className="inline-block"
          key={index}
          variants={AnimationProps}
        >
          &nbsp;
        </motion.span>
      </React.Fragment>
 ))}
  </motion.span>
  {/* site description */}
  <motion.p
    className="text-left text-xl w-1/2 mt-16"
    initial="initial"
    animate="animate"
    aria-hidden="true"
    variants={AnimationDescription}
  >
    {sitedescription}
  </motion.p>
</div>;
{
  /* second text */
}
//...

Here, we have the firstText animation we created in the previous section, which splits each character of every word and animates its opacity and y-axis value with the property AnimationProps. We created a new variable, sitedescription and animation property AnimationDescription. For this animation, we started by specifying initial values of 0 for opacity and 20 for y, and a final value of 1 for opacity and 0 for y.

Also, we added a delay of 3 seconds and made use the type spring with a stiffness of 120. This makes the animation behave like a spring streching passed its intended position on the y-axis and then returning to the y-axis value of 0. This new animation is attached to the paragraph element and here, the entire text is animated at the same time unlike the first animation where we animate individual letters on the y-axis.

Creating loops with animation control

In this section, we will integrate controls into our animations and also learn how best animation start and end events can be handled.

const controls = useAnimation();
const viewPort = useInView(ref, {
  amount: 0.5,
});
useEffect(() => {
  let timeout: NodeJS.Timeout;
  const showText = () => {
    controls.start("animate");
    timeout = setTimeout(async () => {
      // waut to complete the animation
      await controls.start("initial");
      // reset the animation
      controls.start("animate");
 }, 10000);
 };
  if (viewPort) {
    showText();
 }
  if (!viewPort) {
    controls.start("initial");
 }
  return () => {
    clearTimeout(timeout);
 };
}, [viewPort]);

//...
<div className="h-screen">
  <span className="sr-only">{secondText}</span>
  <motion.span
    ref={ref}
    arial-hidden="true"
    className="text-8xl font-bold text-center"
    initial="initial"
    animate={controls}
    variants={{
      animate: {
        transition: {
          staggerChildren: 0.1,
 },
 },
      initial: {},
 }}
  >
    {secondText.split(" ").map((word, index) => (
      <React.Fragment key={index}>
        {word.split("").map((letter, index) => (
          <motion.span
            className="inline-block"
            key={index}
            variants={AnimationProps}
          >
            {letter}
          </motion.span>
 ))}
        <motion.span
          className="inline-block"
          key={index}
          variants={AnimationProps}
        >
          &nbsp;
        </motion.span>
      </React.Fragment>
 ))}
  </motion.span>
</div>

In the code block above, we first created a variable controls which uses the Framer useAnimation() hook. Using this hook we will detect if an element with the ref property has entered the viewport and also added a repeat for the animation using setTimeout. When the element with the ref enters the viewport, we call a function showText(). In this function, we start the animation with controls.start("animate") and run asynchronous function to repeat the animation. The reason for this async function is to ensure that the animation states do not overlap and the animation repeats only after the initial state has been completed.

Everytime there is a change in viewPort, the useEffect hook checks if the element is in view and executes the animation if true. The animation is also set to repeat after a delay of 10 seconds.

Building a landing section with staggered animations

In this section, we will create a landing section and use staggered animation to choreograph the presentation of site content. First, we will create a navigation component, Nav.tsx, and add the following code to it:

"use client";
import { motion } from "framer-motion";
import React from "react";
const Nav = () => {
  return (
    <div className="w-full flex justify-center">
      <motion.div
        className="w-4/5 bg-white text-slate-900 font-medium flex justify-between items-center px-6 py-3 rounded-2xl mt-12"
        initial={{ y: -100 }}
        animate={{ y: 0 }}
        transition={{ duration: 0.5 }}
      >
        <h1>Storytelling</h1>
        <div className="flex space-x-4 items-center">
          <a href="#" className="hover:text-slate-900">
 Home
          </a>
          <a href="#" className="hover:text-slate-900">
 About
          </a>
          <a href="#" className="hover:text-slate-900">
 Services
          </a>
          <a href="#" className="hover:text-slate-900">
 Contact
          </a>
          {/* cta */}
          <button className="bg-slate-900 text-white px-4 py-2 rounded-md">
 Get Started
          </button>
        </div>
      </motion.div>
    </div>
 );
};
export default Nav;

This component uses the Framer motion component to animate the element along the y-axis on page load. Here, we have a div which will serve as a navigation bar, and we are using the motion component to animate this container on the y-axis from an initial value of -100 to 0 using the initial and animate props, and within a duration of 0.5 seconds. We also have four anchor elements and a button for the nav menu.

Next, we will create a call-to-action component for the buttons called CTA.tsx:

"use client";
import React from "react";
import { motion } from "framer-motion";
const Ctas = () => {
  return (
    <div className="flex items-center gap-4 mt-12">
      <motion.button
        className="bg-white text-slate-900 px-4 py-2 rounded-md"
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        transition={{ duration: 1, delay: 4.3 }}
      >
 Get Started
      </motion.button>
      <motion.button
        className="border-white border text-slat-900 px-4 py-2 rounded-md"
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        transition={{ duration: 1, delay: 4.8 }}
      >
 Learn More
      </motion.button>
    </div>
 );
};
export default Ctas;

In the code block above, we have two buttons that use the motion component. For both buttons, the opacity values change from an initial opacity of 0 to a final value of 1. The animation runs for a second and starts after a specified delay. We have a specified animation duration and a delay to make the buttons appear one after the other.

In the StaggeredAnimations.tsx component, make the following changes:

"use client";
import React, { useEffect, useRef } from "react";
import { motion, useAnimation, useInView } from "framer-motion";
import Ctas from "./Ctas";
const StaggeredAnimations = () => {
  //... variables and functions
  return (
    <div>
      {/* staggered text animations */}
      {/* for screen readers */}
      <div className="h-full flex flex-col items-center gap-8">
 //...
        <Ctas />
      </div>
    </div>
 );
};
export default StaggeredAnimations;

Here, we have added the call-to-action component Ctas within the StaggeredAnimations component, after the staggered text animations we created at the start of this article, present in the file. This way, the buttons come after the hero texts on the landing section.

Finally, we will return the Nav and StaggeredAnimations.tsx components in the root page of our application:

import Nav from "@/components/Nav";
import StaggeredAnimations from "@/components/StaggeredAnimations";
export default function Home() {
  return (
    <main className="flex flex-col items-center justify-between px-24">
      <Nav />
      <StaggeredAnimations />
    </main>
 );
}

In the code above, we added the Nav and StaggeredAnimations components to a main element tag and added some styles to display the components in a flex-box. Now, if we run the application, we will get the following results:

Conclusion

Hurray, you’ve made it to the end of this article! In this article, we covered the concept of staggered animations, delving into its applications and benefits, and also discussed code approaches to staggered animation in a web application using Framer Motions.

Truly understand users experience

See every user interaction, feel every frustration and track all hesitations with OpenReplay — the open-source digital experience platform. It can be self-hosted in minutes, giving you complete control over your customer data. . Check our GitHub repo and join the thousands of developers in our community..

OpenReplay