{"id":9759,"date":"2026-06-08T08:50:05","date_gmt":"2026-06-08T13:50:05","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=9759"},"modified":"2026-06-08T08:50:06","modified_gmt":"2026-06-08T13:50:06","slug":"scrubbable-staggered-animation-with-css-function","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/scrubbable-staggered-animation-with-css-function\/","title":{"rendered":"Scrubbable Staggered Animation with CSS @function"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Everyone who has spent time creating staggered effects with CSS has reached for <code>animation-delay<\/code> or <code>transition-delay<\/code> at some point. Vary the delay slightly for each item, and you get that satisfying cascading effect. It works. But it&#8217;s not transferable. You can&#8217;t scrub through it, tie it to scroll, or link it to any external progress value. Each element lives in its own isolated timeline.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There&#8217;s a different way to think about staggered animation \u2014 not as a collection of independently timed objects, but as a single predictable, holistic effect driven by one progress value. You can then easily connect that progress value to an animation, scroll progress, or any other input to drive the staggering.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\">In this article, we will learn how this can be done using a mathematical formula, which I&#8217;ll call the&nbsp;<em>stagger formul<\/em>a<\/span>. We&#8217;ll build it step by step, understand exactly how it works, and implement it in CSS using the new <code>@function<\/code> rule. Plus, have some fun.<\/p>\n\n\n\n<p class=\"learn-more wp-block-paragraph\"><strong>A Quick Note on Browser Support<\/strong><br>Examples in this article use newer CSS features like <code>@function<\/code> rule and <code>if()<\/code>, <code>sibling-index()<\/code>, <code>sibling-count()<\/code> functions. These currently work best in the latest Chrome\/Edge (as of May 2026), while support in Firefox and Safari is still limited or missing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">A Brief Intro to the Stagger Formula<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Imagine <math data-latex=\"m\"><semantics><mi>m<\/mi><annotation encoding=\"application\/x-tex\">m<\/annotation><\/semantics><\/math> as the animation progress of the staggered animation. It&#8217;s not the animation progress of an individual object. It&#8217;s the progress of the whole animation. And you can update <math data-latex=\"m\"><semantics><mi>m<\/mi><annotation encoding=\"application\/x-tex\">m<\/annotation><\/semantics><\/math>&#8216;s value to drive the animation in a particular direction, somewhat like scrubbing the timeline in a video editor. You can link <math data-latex=\"m\"><semantics><mi>m<\/mi><annotation encoding=\"application\/x-tex\">m<\/annotation><\/semantics><\/math> to <code>@keyframes<\/code>, transition, scroll-driven animations, or any other kind of motion. It opens up a lot of possibilities that are not possible with thinking in terms of individual objects separately.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The stagger formula makes it possible. You give it <math data-latex=\"m\"><semantics><mi>m<\/mi><annotation encoding=\"application\/x-tex\">m<\/annotation><\/semantics><\/math> and a few more bits of data, it gives you back the animation progress of an individual object you are interested in. It looks like this:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>p<\/mi><mi>i<\/mi><\/msub><mo>=<\/mo><mtext>clamp<\/mtext><mspace width=\"-0.1667em\" style=\"margin-left:-0.1667em;\"><\/mspace><mrow><mo fence=\"true\" form=\"prefix\">(<\/mo><mn>0<\/mn><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mtext>lerp<\/mtext><mspace width=\"-0.1667em\" style=\"margin-left:-0.1667em;\"><\/mspace><mrow><mo fence=\"true\" form=\"prefix\">(<\/mo><mi>x<\/mi><mo>+<\/mo><mfrac><mi>u<\/mi><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mi>y<\/mi><mo>+<\/mo><mfrac><mi>v<\/mi><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mi>m<\/mi><mo fence=\"true\" form=\"postfix\">)<\/mo><\/mrow><mo>\u2212<\/mo><mfrac><mi>i<\/mi><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mn>1<\/mn><mo fence=\"true\" form=\"postfix\">)<\/mo><\/mrow><\/mrow><annotation encoding=\"application\/x-tex\">p_i = \\text{clamp}\\!\\left(0,\\ \\text{lerp}\\!\\left(x + \\frac{u}{k},\\ y + \\frac{v}{k},\\ m\\right) &#8211; \\frac{i}{k},\\ 1\\right)<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">In the following CodePen, you can play with different parameters to get a feel for them.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_PwbGEgj\" src=\"\/\/codepen.io\/anon\/embed\/PwbGEgj?height=800&amp;theme-id=1&amp;slug-hash=PwbGEgj&amp;default-tab=result\" height=\"800\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed PwbGEgj\" title=\"CodePen Embed PwbGEgj\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe loading=\"lazy\" title=\"VideoPress Video Player\" aria-label=\"VideoPress Video Player\" width=\"500\" height=\"281\" src=\"https:\/\/videopress.com\/embed\/TtEGUZ80?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0\" frameborder=\"0\" allowfullscreen data-resize-to-parent=\"true\" allow=\"clipboard-write\"><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1770107250'><\/script><\/div>\n\t\t\t<figcaption><\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p class=\"wp-block-paragraph\">Notice the objects with muted colors on both sides and how the staggered animation can exist there. Also note that there is no slider for <math data-latex=\"i\"><semantics><mi>i<\/mi><annotation encoding=\"application\/x-tex\">i<\/annotation><\/semantics><\/math>. It comes from the individual objects and is shown below them. &nbsp;The dashed-gray-bordered box highlights the elements that will animate as you move any slider.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It\u2019s indeed a huge formula and can look intimidating, but it\u2019s easier to understand than it may seem. We\u2019ll build the formula later in this article. First, we need to understand how the animation actually works.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How Our Staggered Animation Works<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The kind of staggered animation we are going to achieve is different from traditional ones because we don&#8217;t include time in our considerations. We figure out the relationship of the progress of the objects we are animating. It&#8217;s important to understand this relationship first to understand the formula.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First, I want you to know a few terms and conventions that I\u2019ll use throughout this article.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Animation order<\/strong> is an integer associated with an object that defines when it starts animation relative to other objects. Objects with a higher animation order start later, while objects with the same animation order start simultaneously. Each object under a staggered animation has an animation order. In most cases, the indices of the objects(retrieved via <code>sibling-index()<\/code>) are exactly what we want as their animation orders.<\/li>\n\n\n\n<li>&nbsp;<strong>Infinite ordered objects<\/strong> is an infinite sequence of objects with animation orders from low to high (left to right), corresponding to the integers on the number line.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Our real objects are only a very small part of it. Although it is not possible to have truly infinite objects in a computer, they provide a robust mathematical foundation for running staggered animations and reasoning about the process. The stagger effect will most likely originate in these imaginary objects, pass through our real objects (the middle part in our demo), and continue into the imaginary objects on the other side.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"615\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/iKxLvBVg.gif?resize=1024%2C615&#038;ssl=1\" alt=\"\" class=\"wp-image-9911\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/iKxLvBVg.gif?resize=1024%2C615&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/iKxLvBVg.gif?resize=300%2C180&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/iKxLvBVg.gif?resize=768%2C461&amp;ssl=1 768w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">Flow of stagger effect<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We use the convention of representing <strong>animation progress<\/strong> from start to finish within the <math data-latex=\"[0, 1]\"><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">[<\/mo><mn>0<\/mn><mo separator=\"true\">,<\/mo><mn>1<\/mn><mo form=\"postfix\" stretchy=\"false\">]<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">[0, 1]<\/annotation><\/semantics><\/math> range, both for individual objects and for the staggered animation as a whole.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Alright! Now we are ready to start defining the staggered animation. Suppose we have a sequence of infinite ordered objects where:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>At any time, exactly <math data-latex=\"k\"><semantics><mi>k<\/mi><annotation encoding=\"application\/x-tex\">k<\/annotation><\/semantics><\/math> consecutive objects are animating (i.e., changing their animation progress).<\/li>\n\n\n\n<li>These <math data-latex=\"k\"><semantics><mi>k<\/mi><annotation encoding=\"application\/x-tex\">k<\/annotation><\/semantics><\/math> objects have decreasing progress from left to right, with a constant gap <math data-latex=\"d\"><semantics><mi>d<\/mi><annotation encoding=\"application\/x-tex\">d<\/annotation><\/semantics><\/math>, and all of their progress values increase at the same rate.<\/li>\n\n\n\n<li>All objects to the left of these <math data-latex=\"k\"><semantics><mi>k<\/mi><annotation encoding=\"application\/x-tex\">k<\/annotation><\/semantics><\/math> objects have progress <math data-latex=\"1\"><semantics><mn>1<\/mn><annotation encoding=\"application\/x-tex\">1<\/annotation><\/semantics><\/math>, and all objects to the right have progress <math data-latex=\"0\"><semantics><mn>0<\/mn><annotation encoding=\"application\/x-tex\">0<\/annotation><\/semantics><\/math>.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Let the infinite ordered sequence of objects be:<\/p>\n\n\n\n<div class=\"wp-block-math ticss-c89a9344\"><math display=\"block\"><semantics><mrow><mo>\u22ef<\/mo><mspace width=\"0.1667em\"><\/mspace><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><msub><mi>O<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>3<\/mn><\/mrow><\/msub><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><msub><mi>O<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>2<\/mn><\/mrow><\/msub><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><msub><mi>O<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>1<\/mn><\/mrow><\/msub><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><msub><mi>O<\/mi><mn>0<\/mn><\/msub><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><msub><mi>O<\/mi><mn>1<\/mn><\/msub><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><msub><mi>O<\/mi><mn>2<\/mn><\/msub><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><msub><mi>O<\/mi><mn>3<\/mn><\/msub><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mo>\u22ef<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\cdots,\\ O_{-3},\\ O_{-2},\\ O_{-1},\\ O_0,\\ O_1,\\ O_2,\\ O_3,\\ \\cdots\n<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Let us assume that the <math data-latex=\"k\"><semantics><mi>k<\/mi><annotation encoding=\"application\/x-tex\">k<\/annotation><\/semantics><\/math> objects currently animating start at <math data-latex=\"O_1\"><semantics><msub><mi>O<\/mi><mn>1<\/mn><\/msub><annotation encoding=\"application\/x-tex\">O_1<\/annotation><\/semantics><\/math>. Then at start time <math data-latex=\"O_i\"><semantics><msub><mi>O<\/mi><mi>i<\/mi><\/msub><annotation encoding=\"application\/x-tex\">O_i<\/annotation><\/semantics><\/math> with <math data-latex=\"i \\le 0\"><semantics><mrow><mi>i<\/mi><mo>\u2264<\/mo><mn>0<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">i \\le 0<\/annotation><\/semantics><\/math> have progress <math data-latex=\"1\"><semantics><mn>1<\/mn><annotation encoding=\"application\/x-tex\">1<\/annotation><\/semantics><\/math>, and all <math data-latex=\"O_i\"><semantics><msub><mi>O<\/mi><mi>i<\/mi><\/msub><annotation encoding=\"application\/x-tex\">O_i<\/annotation><\/semantics><\/math> with <math data-latex=\"i &gt; k\"><semantics><mrow><mi>i<\/mi><mo>&gt;<\/mo><mi>k<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">i &gt; k<\/annotation><\/semantics><\/math> have progress <math data-latex=\"0\"><semantics><mn>0<\/mn><annotation encoding=\"application\/x-tex\">0<\/annotation><\/semantics><\/math>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When <math data-latex=\"O_1\"><semantics><msub><mi>O<\/mi><mn>1<\/mn><\/msub><annotation encoding=\"application\/x-tex\">O_1<\/annotation><\/semantics><\/math> reaches progress <math data-latex=\"1\"><semantics><mn>1<\/mn><annotation encoding=\"application\/x-tex\">1<\/annotation><\/semantics><\/math>, the progress of <math data-latex=\"O_1\"><semantics><msub><mi>O<\/mi><mn>1<\/mn><\/msub><annotation encoding=\"application\/x-tex\">O_1<\/annotation><\/semantics><\/math> to <math data-latex=\"O_k\"><semantics><msub><mi>O<\/mi><mi>k<\/mi><\/msub><annotation encoding=\"application\/x-tex\">O_k<\/annotation><\/semantics><\/math> objects look like:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><th><math data-latex=\"O_1\"><semantics><msub><mi>O<\/mi><mn>1<\/mn><\/msub><annotation encoding=\"application\/x-tex\">O_1<\/annotation><\/semantics><\/math><\/th><th><math data-latex=\"O_2\"><semantics><msub><mi>O<\/mi><mn>2<\/mn><\/msub><annotation encoding=\"application\/x-tex\">O_2<\/annotation><\/semantics><\/math><\/th><th><math data-latex=\"O_3\"><semantics><msub><mi>O<\/mi><mn>3<\/mn><\/msub><annotation encoding=\"application\/x-tex\">O_3<\/annotation><\/semantics><\/math><\/th><th>&#8230;<\/th><th><math data-latex=\"O_k\"><semantics><msub><mi>O<\/mi><mi>k<\/mi><\/msub><annotation encoding=\"application\/x-tex\">O_k<\/annotation><\/semantics><\/math><\/th><\/tr><tr><td><math data-latex=\"1\"><semantics><mn>1<\/mn><annotation encoding=\"application\/x-tex\">1<\/annotation><\/semantics><\/math><\/td><td><math data-latex=\"1-d\"><semantics><mrow><mn>1<\/mn><mo>\u2212<\/mo><mi>d<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">1-d<\/annotation><\/semantics><\/math><\/td><td><math data-latex=\"1-2d\"><semantics><mrow><mn>1<\/mn><mo>\u2212<\/mo><mn>2<\/mn><mi>d<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">1-2d<\/annotation><\/semantics><\/math><\/td><td>\u2026<\/td><td><math data-latex=\"1-(k-1)d\"><semantics><mrow><mn>1<\/mn><mo>\u2212<\/mo><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>k<\/mi><mo>\u2212<\/mo><mn>1<\/mn><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mi>d<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">1-(k-1)d<\/annotation><\/semantics><\/math><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We assume all of these are in the valid animation progress range, that is, <math data-latex=\"[0, 1]\"><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">[<\/mo><mn>0<\/mn><mo separator=\"true\">,<\/mo><mn>1<\/mn><mo form=\"postfix\" stretchy=\"false\">]<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">[0, 1]<\/annotation><\/semantics><\/math>.<\/p>\n\n\n\n<p class=\"learn-more wp-block-paragraph\">This configuration is the maximal valid configuration under the constraints (constant gap and equal rate), because increasing all progresses further would push <math data-latex=\"O_1\"><semantics><msub><mi>O<\/mi><mn>1<\/mn><\/msub><annotation encoding=\"application\/x-tex\">O_1<\/annotation><\/semantics><\/math> beyond <math data-latex=\"1\"><semantics><mn>1<\/mn><annotation encoding=\"application\/x-tex\">1<\/annotation><\/semantics><\/math>, which is not allowed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Currently, we do not know the progress gap between <math data-latex=\"O_k\"><semantics><msub><mi>O<\/mi><mi>k<\/mi><\/msub><annotation encoding=\"application\/x-tex\">O_k<\/annotation><\/semantics><\/math> and <math data-latex=\"O_{k+1}\"><semantics><msub><mi>O<\/mi><mrow><mi>k<\/mi><mo>+<\/mo><mn>1<\/mn><\/mrow><\/msub><annotation encoding=\"application\/x-tex\">O_{k+1}<\/annotation><\/semantics><\/math> (which has progress <math data-latex=\"0\"><semantics><mn>0<\/mn><annotation encoding=\"application\/x-tex\">0<\/annotation><\/semantics><\/math>). However, if this gap is <math data-latex=\"d\"><semantics><mi>d<\/mi><annotation encoding=\"application\/x-tex\">d<\/annotation><\/semantics><\/math>, we can shift our focus to the <math data-latex=\"k\"><semantics><mi>k<\/mi><annotation encoding=\"application\/x-tex\">k<\/annotation><\/semantics><\/math> objects starting from <math data-latex=\"O_2\"><semantics><msub><mi>O<\/mi><mn>2<\/mn><\/msub><annotation encoding=\"application\/x-tex\">O_2<\/annotation><\/semantics><\/math> and ending at <math data-latex=\"O_{k+1}\"><semantics><msub><mi>O<\/mi><mrow><mi>k<\/mi><mo>+<\/mo><mn>1<\/mn><\/mrow><\/msub><annotation encoding=\"application\/x-tex\">O_{k+1}<\/annotation><\/semantics><\/math>, because they will all have a gap of <math data-latex=\"d\"><semantics><mi>d<\/mi><annotation encoding=\"application\/x-tex\">d<\/annotation><\/semantics><\/math> between their progress values.<\/p>\n\n\n\n<p class=\"learn-more wp-block-paragraph\">Since <math data-latex=\"O_{k+1} = 0\"><semantics><mrow><msub><mi>O<\/mi><mrow><mi>k<\/mi><mo>+<\/mo><mn>1<\/mn><\/mrow><\/msub><mo>=<\/mo><mn>0<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">O_{k+1} = 0<\/annotation><\/semantics><\/math>, this set of <math data-latex=\"k\"><semantics><mi>k<\/mi><annotation encoding=\"application\/x-tex\">k<\/annotation><\/semantics><\/math> objects is already at its minimal valid configuration. Any decrease would push <math data-latex=\"O_{k+1}\"><semantics><msub><mi>O<\/mi><mrow><mi>k<\/mi><mo>+<\/mo><mn>1<\/mn><\/mrow><\/msub><annotation encoding=\"application\/x-tex\">O_{k+1}<\/annotation><\/semantics><\/math> below <math data-latex=\"0\"><semantics><mn>0<\/mn><annotation encoding=\"application\/x-tex\">0<\/annotation><\/semantics><\/math>, which is not allowed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So we define <math data-latex=\"d\"><semantics><mi>d<\/mi><annotation encoding=\"application\/x-tex\">d<\/annotation><\/semantics><\/math> to be equal to the gap between <math data-latex=\"O_k\"><semantics><msub><mi>O<\/mi><mi>k<\/mi><\/msub><annotation encoding=\"application\/x-tex\">O_k<\/annotation><\/semantics><\/math> and <math data-latex=\"O_{k+1}\"><semantics><msub><mi>O<\/mi><mrow><mi>k<\/mi><mo>+<\/mo><mn>1<\/mn><\/mrow><\/msub><annotation encoding=\"application\/x-tex\">O_{k+1}<\/annotation><\/semantics><\/math>, which is:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>d<\/mi><mo>=<\/mo><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mn>1<\/mn><mo>\u2212<\/mo><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>k<\/mi><mo>\u2212<\/mo><mn>1<\/mn><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mi>d<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>\u2212<\/mo><mn>0<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">d = (1 &#8211; (k-1)d) &#8211; 0<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">We can now easily find <math data-latex=\"d\"><semantics><mi>d<\/mi><annotation encoding=\"application\/x-tex\">d<\/annotation><\/semantics><\/math>&#8216;s actual value:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>d<\/mi><mo>=<\/mo><mn>1<\/mn><mo>\u2212<\/mo><mi>k<\/mi><mi>d<\/mi><mo>+<\/mo><mi>d<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">d = 1 &#8211; kd + d<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>k<\/mi><mi>d<\/mi><mo>=<\/mo><mn>1<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">kd = 1<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>d<\/mi><mo>=<\/mo><mfrac><mn>1<\/mn><mi>k<\/mi><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">d = \\frac{1}{k}<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Knowing the exact value of <math data-latex=\"d\"><semantics><mi>d<\/mi><annotation encoding=\"application\/x-tex\">d<\/annotation><\/semantics><\/math> will later help us in building the stagger formula.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Thus, this new group can now evolve in the same way until <math data-latex=\"O_2\"><semantics><msub><mi>O<\/mi><mn>2<\/mn><\/msub><annotation encoding=\"application\/x-tex\">O_2<\/annotation><\/semantics><\/math> reaches <math data-latex=\"1\"><semantics><mn>1<\/mn><annotation encoding=\"application\/x-tex\">1<\/annotation><\/semantics><\/math>, and the process repeats. This creates a staggered animation that propagates smoothly across the sequence.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Building the Formula<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Our foundation is now mathematically solid. We are ready to build the formula.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let <math data-latex=\"i\"><semantics><mi>i<\/mi><annotation encoding=\"application\/x-tex\">i<\/annotation><\/semantics><\/math> represent the animation order of the objects in <em>infinite ordered objects<\/em> sequence.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let <math data-latex=\"p_i\"><semantics><msub><mi>p<\/mi><mi>i<\/mi><\/msub><annotation encoding=\"application\/x-tex\">p_i<\/annotation><\/semantics><\/math> represent the animation progress of <math data-latex=\"i\"><semantics><mi>i<\/mi><annotation encoding=\"application\/x-tex\">i<\/annotation><\/semantics><\/math>th object. This is what we want to find out for a given <math data-latex=\"m\"><semantics><mi>m<\/mi><annotation encoding=\"application\/x-tex\">m<\/annotation><\/semantics><\/math> (the overall staggered animation progress).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We know that the difference between the animation progress of any two adjacent objects in the currently animating <math data-latex=\"k\"><semantics><mi>k<\/mi><annotation encoding=\"application\/x-tex\">k<\/annotation><\/semantics><\/math> objects is <math data-latex=\"\\frac{1}{k}\"><semantics><mfrac><mn>1<\/mn><mi>k<\/mi><\/mfrac><annotation encoding=\"application\/x-tex\">\\frac{1}{k}<\/annotation><\/semantics><\/math>. So let&#8217;s create a new sequence <math data-latex=\"p'_i\"><semantics><msubsup><mi>p<\/mi><mi>i<\/mi><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/msubsup><annotation encoding=\"application\/x-tex\">p&#8217;_i<\/annotation><\/semantics><\/math>:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mo>\u22ef<\/mo><mspace width=\"0.1667em\"><\/mspace><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><msubsup><mi>p<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>2<\/mn><\/mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/msubsup><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><msubsup><mi>p<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>1<\/mn><\/mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/msubsup><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><msubsup><mi>p<\/mi><mn>0<\/mn><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/msubsup><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><msubsup><mi>p<\/mi><mn>1<\/mn><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/msubsup><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><msubsup><mi>p<\/mi><mn>2<\/mn><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/msubsup><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mo>\u22ef<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\cdots,\\ p&#8217;_{-2},\\ p&#8217;_{-1},\\ p&#8217;_{0},\\ p&#8217;_{1},\\ p&#8217;_{2},\\ \\cdots<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">where <math data-latex=\"p'_{i + 1} - p'_i = \\frac{1}{k}\"><semantics><mrow><msubsup><mi>p<\/mi><mrow><mi>i<\/mi><mo>+<\/mo><mn>1<\/mn><\/mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/msubsup><mo>\u2212<\/mo><msubsup><mi>p<\/mi><mi>i<\/mi><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/msubsup><mo>=<\/mo><mfrac><mn>1<\/mn><mi>k<\/mi><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">p&#8217;_{i + 1} &#8211; p&#8217;_i = \\frac{1}{k}<\/annotation><\/semantics><\/math>. We can get that by multiplying <math data-latex=\"i\"><semantics><mi>i<\/mi><annotation encoding=\"application\/x-tex\">i<\/annotation><\/semantics><\/math> by <math data-latex=\"1\/k\"><semantics><mrow><mn>1<\/mn><mi>\/<\/mi><mi>k<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">1\/k<\/annotation><\/semantics><\/math>:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msubsup><mi>p<\/mi><mi>i<\/mi><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/msubsup><mo>=<\/mo><mfrac><mi>i<\/mi><mi>k<\/mi><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">p&#8217;_{i} = \\frac{i}{k}<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Which is:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mo>\u22ef<\/mo><mspace width=\"0.1667em\"><\/mspace><mo separator=\"true\">,<\/mo><mfrac><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>2<\/mn><\/mrow><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mfrac><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>1<\/mn><\/mrow><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mfrac><mn>0<\/mn><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mfrac><mn>1<\/mn><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mfrac><mn>2<\/mn><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mo>\u22ef<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\cdots, \\frac{-2}{k}, \\frac{-1}{k}, \\frac{0}{k}, \\frac{1}{k}, \\frac{2}{k}, \\cdots<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">We have also seen that the objects in the currently animating <math data-latex=\"k\"><semantics><mi>k<\/mi><annotation encoding=\"application\/x-tex\">k<\/annotation><\/semantics><\/math> objects are in descending order. Our <math data-latex=\"p'_i\"><semantics><msubsup><mi>p<\/mi><mi>i<\/mi><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/msubsup><annotation encoding=\"application\/x-tex\">p&#8217;_i<\/annotation><\/semantics><\/math> sequence is ascending. So let them descend in a new sequence <math data-latex=\"p''_i\"><semantics><msubsup><mi>p<\/mi><mi>i<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/mrow><\/msubsup><annotation encoding=\"application\/x-tex\">p&#8221;_i<\/annotation><\/semantics><\/math> by subtracting <math data-latex=\"p'_i\"><semantics><msubsup><mi>p<\/mi><mi>i<\/mi><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/msubsup><annotation encoding=\"application\/x-tex\">p&#8217;_i<\/annotation><\/semantics><\/math> from some unknown number <math data-latex=\"q\"><semantics><mi>q<\/mi><annotation encoding=\"application\/x-tex\">q<\/annotation><\/semantics><\/math> which we will figure out soon:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msubsup><mi>p<\/mi><mi>i<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/mrow><\/msubsup><mo>=<\/mo><mi>q<\/mi><mo>\u2212<\/mo><msubsup><mi>p<\/mi><mi>i<\/mi><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/msubsup><\/mrow><annotation encoding=\"application\/x-tex\">p&#8221;_i = q &#8211; p&#8217;_i<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<div class=\"wp-block-group learn-more\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\n<p class=\"wp-block-paragraph\">This is simple trick that you can use to turn an ascending sequence to a descending sequence. For example let&#8217;s you have:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mn>1<\/mn><mo separator=\"true\">,<\/mo><mn>2<\/mn><mo separator=\"true\">,<\/mo><mn>3<\/mn><mo separator=\"true\">,<\/mo><mn>4<\/mn><mo separator=\"true\">,<\/mo><mn>5<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">1, 2, 3, 4, 5<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">If you subtract each of the above number from <math data-latex=\"10\"><semantics><mn>10<\/mn><annotation encoding=\"application\/x-tex\">10<\/annotation><\/semantics><\/math>, you get:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mn>9<\/mn><mo separator=\"true\">,<\/mo><mn>8<\/mn><mo separator=\"true\">,<\/mo><mn>7<\/mn><mo separator=\"true\">,<\/mo><mn>6<\/mn><mo separator=\"true\">,<\/mo><mn>5<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">9, 8, 7, 6, 5<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Note that the numbers can be different from original set of numbers.<\/p>\n<\/div><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Now, if we replace <math data-latex=\"p'_i\"><semantics><msubsup><mi>p<\/mi><mi>i<\/mi><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/msubsup><annotation encoding=\"application\/x-tex\">p&#8217;_i<\/annotation><\/semantics><\/math> with its value, we get<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msubsup><mi>p<\/mi><mi>i<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/mrow><\/msubsup><mo>=<\/mo><mi>q<\/mi><mo>\u2212<\/mo><mfrac><mi>i<\/mi><mi>k<\/mi><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">p&#8221;_i = q &#8211; \\frac{i}{k}<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">So <math data-latex=\"p''_i\"><semantics><msubsup><mi>p<\/mi><mi>i<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/mrow><\/msubsup><annotation encoding=\"application\/x-tex\">p&#8221;_i<\/annotation><\/semantics><\/math> looks like this for each of <math data-latex=\"i\"><semantics><mi>i<\/mi><annotation encoding=\"application\/x-tex\">i<\/annotation><\/semantics><\/math>th object in our infinite ordered objects:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mo>\u22ef<\/mo><mspace width=\"0.1667em\"><\/mspace><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mi>q<\/mi><mo>\u2212<\/mo><mfrac><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>2<\/mn><\/mrow><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mi>q<\/mi><mo>\u2212<\/mo><mfrac><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>1<\/mn><\/mrow><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mi>q<\/mi><mo>\u2212<\/mo><mfrac><mn>0<\/mn><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mi>q<\/mi><mo>\u2212<\/mo><mfrac><mn>1<\/mn><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mi>q<\/mi><mo>\u2212<\/mo><mfrac><mn>2<\/mn><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mo>\u22ef<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\cdots,\\ q-\\frac{-2}{k},\\ q-\\frac{-1}{k},\\ q-\\frac{0}{k},\\ q-\\frac{1}{k},\\ q-\\frac{2}{k},\\ \\cdots<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Note that the difference between two adjacent <math data-latex=\"p''_i\"><semantics><msubsup><mi>p<\/mi><mi>i<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/mrow><\/msubsup><annotation encoding=\"application\/x-tex\">p&#8221;_i<\/annotation><\/semantics><\/math> is still <math data-latex=\"\\frac{1}{k}\"><semantics><mfrac><mn>1<\/mn><mi>k<\/mi><\/mfrac><annotation encoding=\"application\/x-tex\">\\frac{1}{k}<\/annotation><\/semantics><\/math>.<\/p>\n\n\n\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained\">\n<div class=\"wp-block-group learn-more\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained\">\n<p class=\"wp-block-paragraph\">Can you see that somewhere among our infinitely ordered objects, there are progress values within the valid range <math data-latex=\"[0,1]\"><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">[<\/mo><mn>0,1<\/mn><mo form=\"postfix\" stretchy=\"false\">]<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">[0,1]<\/annotation><\/semantics><\/math>, each separated by a gap of <math data-latex=\"\\frac{1}{k}\"><semantics><mfrac><mn>1<\/mn><mi>k<\/mi><\/mfrac><annotation encoding=\"application\/x-tex\">\\frac{1}{k}<\/annotation><\/semantics><\/math>, in this sea of out of range animation progress values?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s look at a concrete example with real values. Let <math data-latex=\"k = 3\"><semantics><mrow><mi>k<\/mi><mo>=<\/mo><mn>3<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">k = 3<\/annotation><\/semantics><\/math> and <math data-latex=\"q = 1\"><semantics><mrow><mi>q<\/mi><mo>=<\/mo><mn>1<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">q = 1<\/annotation><\/semantics><\/math>. Then a subset of the values of <math data-latex=\"p''_i\"><semantics><msubsup><mi>p<\/mi><mi>i<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/mrow><\/msubsup><annotation encoding=\"application\/x-tex\">p&#8221;_i<\/annotation><\/semantics><\/math> become:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"894\" height=\"1024\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/ex2g0RZ1.jpeg?resize=894%2C1024&#038;ssl=1\" alt=\"\" class=\"wp-image-9784\" style=\"aspect-ratio:0.873048763641387;width:512px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/ex2g0RZ1.jpeg?resize=894%2C1024&amp;ssl=1 894w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/ex2g0RZ1.jpeg?resize=262%2C300&amp;ssl=1 262w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/ex2g0RZ1.jpeg?resize=768%2C880&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/ex2g0RZ1.jpeg?resize=1340%2C1536&amp;ssl=1 1340w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/ex2g0RZ1.jpeg?w=1520&amp;ssl=1 1520w\" sizes=\"auto, (max-width: 894px) 100vw, 894px\" \/><figcaption class=\"wp-element-caption\">A table highlighting <math data-latex=\"p''_i\"><semantics><msubsup><mi>p<\/mi><mi>i<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/mrow><\/msubsup><annotation encoding=\"application\/x-tex\">p&#8221;_i<\/annotation><\/semantics><\/math> values that intersect with <math data-latex=\"p_i\"><semantics><msub><mi>p<\/mi><mi>i<\/mi><\/msub><annotation encoding=\"application\/x-tex\">p_i<\/annotation><\/semantics><\/math> values<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">We can see that the progress values within the <math data-latex=\"[0,1]\"><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">[<\/mo><mn>0,1<\/mn><mo form=\"postfix\" stretchy=\"false\">]<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">[0,1]<\/annotation><\/semantics><\/math> range start at <math data-latex=\"i = 0\"><semantics><mrow><mi>i<\/mi><mo>=<\/mo><mn>0<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">i = 0<\/annotation><\/semantics><\/math> and end at <math data-latex=\"i = 3\"><semantics><mrow><mi>i<\/mi><mo>=<\/mo><mn>3<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">i = 3<\/annotation><\/semantics><\/math>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We can also see that objects to the left of this range have <math data-latex=\"p''_i &gt; 1\"><semantics><mrow><msubsup><mi>p<\/mi><mi>i<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/mrow><\/msubsup><mo>&gt;<\/mo><mn>1<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">p&#8221;_i &gt; 1<\/annotation><\/semantics><\/math>, while objects to the right have <math data-latex=\"p''_i &lt; 0\"><semantics><mrow><msubsup><mi>p<\/mi><mi>i<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/mrow><\/msubsup><mo>&lt;<\/mo><mn>0<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">p&#8221;_i &lt; 0<\/annotation><\/semantics><\/math>. We can eliminate this issue using <code>clamp()<\/code> to flatten the values on each of these two sides to <math data-latex=\"1\"><semantics><mn>1<\/mn><annotation encoding=\"application\/x-tex\">1<\/annotation><\/semantics><\/math> and <math data-latex=\"0\"><semantics><mn>0<\/mn><annotation encoding=\"application\/x-tex\">0<\/annotation><\/semantics><\/math> respectively.<\/p>\n<\/div><\/div>\n<\/div><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">To figure out <math data-latex=\"q\"><semantics><mi>q<\/mi><annotation encoding=\"application\/x-tex\">q<\/annotation><\/semantics><\/math> we need to know to exactly where to start and end our animation. Let&#8217;s introduce some more variables for pointing these positions:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><math data-latex=\"u\"><semantics><mi>u<\/mi><annotation encoding=\"application\/x-tex\">u<\/annotation><\/semantics><\/math>: The animation order of an object where the staggering animation starts.\n<ul class=\"wp-block-list has-custom-css wp-custom-css-9c0f3b93\">\n<li><math data-latex=\"x\"><semantics><mi>x<\/mi><annotation encoding=\"application\/x-tex\">x<\/annotation><\/semantics><\/math>: The animation progress of <math data-latex=\"u\"><semantics><mi>u<\/mi><annotation encoding=\"application\/x-tex\">u<\/annotation><\/semantics><\/math>th object at that time.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><math data-latex=\"v\"><semantics><mi>v<\/mi><annotation encoding=\"application\/x-tex\">v<\/annotation><\/semantics><\/math>: The animation order of an object where the staggering animation ends.\n<ul class=\"wp-block-list\">\n<li><math data-latex=\"y\"><semantics><mi>y<\/mi><annotation encoding=\"application\/x-tex\">y<\/annotation><\/semantics><\/math>: The animation progress of <math data-latex=\"v\"><semantics><mi>v<\/mi><annotation encoding=\"application\/x-tex\">v<\/annotation><\/semantics><\/math>th object at that time.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Now, when <math data-latex=\"m = 0\"><semantics><mrow><mi>m<\/mi><mo>=<\/mo><mn>0<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">m = 0<\/annotation><\/semantics><\/math>, the following must hold:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msubsup><mi>p<\/mi><mi>u<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/mrow><\/msubsup><mo>=<\/mo><mi>x<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">p&#8221;_u = x<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Now we can easily figure out <math data-latex=\"q\"><semantics><mi>q<\/mi><annotation encoding=\"application\/x-tex\">q<\/annotation><\/semantics><\/math> by replacing <math data-latex=\"p''_u\"><semantics><msubsup><mi>p<\/mi><mi>u<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/mrow><\/msubsup><annotation encoding=\"application\/x-tex\">p&#8221;_u<\/annotation><\/semantics><\/math> with its value:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>q<\/mi><mo>\u2212<\/mo><mfrac><mi>u<\/mi><mi>k<\/mi><\/mfrac><mo>=<\/mo><mi>x<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">q &#8211; \\frac{u}{k} = x<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>q<\/mi><mo>=<\/mo><mi>x<\/mi><mo>+<\/mo><mfrac><mi>u<\/mi><mi>k<\/mi><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">q = x + \\frac{u}{k}<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Similarly when <math data-latex=\"m = 1\"><semantics><mrow><mi>m<\/mi><mo>=<\/mo><mn>1<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">m = 1<\/annotation><\/semantics><\/math>, <math data-latex=\"q = y + \\frac{v}{k}\"><semantics><mrow><mi>q<\/mi><mo>=<\/mo><mi>y<\/mi><mo>+<\/mo><mfrac><mi>v<\/mi><mi>k<\/mi><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">q = y + \\frac{v}{k}<\/annotation><\/semantics><\/math> because <math data-latex=\"p''_v = y\"><semantics><mrow><msubsup><mi>p<\/mi><mi>v<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/mrow><\/msubsup><mo>=<\/mo><mi>y<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">p&#8221;_v = y<\/annotation><\/semantics><\/math>.<\/p>\n\n\n\n<p class=\"learn-more wp-block-paragraph\">Now for <math data-latex=\"[0, 1]\"><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">[<\/mo><mn>0<\/mn><mo separator=\"true\">,<\/mo><mn>1<\/mn><mo form=\"postfix\" stretchy=\"false\">]<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">[0, 1]<\/annotation><\/semantics><\/math> range of the <math data-latex=\"m\"><semantics><mi>m<\/mi><annotation encoding=\"application\/x-tex\">m<\/annotation><\/semantics><\/math>, the range of <math data-latex=\"q\"><semantics><mi>q<\/mi><annotation encoding=\"application\/x-tex\">q<\/annotation><\/semantics><\/math> becomes <math data-latex=\"[x + \\frac{u}{k},y + \\frac{v}{k}]\"><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">[<\/mo><mi>x<\/mi><mo>+<\/mo><mfrac><mi>u<\/mi><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mi>y<\/mi><mo>+<\/mo><mfrac><mi>v<\/mi><mi>k<\/mi><\/mfrac><mo form=\"postfix\" stretchy=\"false\">]<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">[x + \\frac{u}{k},y + \\frac{v}{k}]<\/annotation><\/semantics><\/math>. We need to do a linear mapping from one range of values to the other so that their ends match together.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We can easily find the value for <math data-latex=\"q\"><semantics><mi>q<\/mi><annotation encoding=\"application\/x-tex\">q<\/annotation><\/semantics><\/math> for any value of <math data-latex=\"m\"><semantics><mi>m<\/mi><annotation encoding=\"application\/x-tex\">m<\/annotation><\/semantics><\/math> in its range using the linear interpolation function <math data-latex=\"\\text{lerp}()\"><semantics><mrow><mtext>lerp<\/mtext><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\text{lerp}()<\/annotation><\/semantics><\/math>:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>q<\/mi><mo>=<\/mo><mtext>lerp<\/mtext><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>x<\/mi><mo>+<\/mo><mfrac><mi>u<\/mi><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mi>y<\/mi><mo>+<\/mo><mfrac><mi>v<\/mi><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mi>m<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">q = \\text{lerp}(x + \\frac{u}{k},\\ y + \\frac{v}{k},\\ m)<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">We will later define <math data-latex=\"\\text{lerp()}\"><semantics><mtext>lerp()<\/mtext><annotation encoding=\"application\/x-tex\">\\text{lerp()}<\/annotation><\/semantics><\/math> using the CSS <code>@function<\/code> rule. What it does can be visualized like this: it uniformly stretches or squeezes the <math data-latex=\"0\"><semantics><mn>0<\/mn><annotation encoding=\"application\/x-tex\">0<\/annotation><\/semantics><\/math> to <math data-latex=\"1\"><semantics><mn>1<\/mn><annotation encoding=\"application\/x-tex\">1<\/annotation><\/semantics><\/math> scale of <math data-latex=\"m\"><semantics><mi>m<\/mi><annotation encoding=\"application\/x-tex\">m<\/annotation><\/semantics><\/math> onto another scale whose lower bound is <math data-latex=\"x + \\frac{u}{k}\"><semantics><mrow><mi>x<\/mi><mo>+<\/mo><mfrac><mi>u<\/mi><mi>k<\/mi><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">x + \\frac{u}{k}<\/annotation><\/semantics><\/math> and upper bound is <math data-latex=\"y + \\frac{v}{k}\"><semantics><mrow><mi>y<\/mi><mo>+<\/mo><mfrac><mi>v<\/mi><mi>k<\/mi><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">y + \\frac{v}{k}<\/annotation><\/semantics><\/math> so that the two scales align. It then returns the corresponding value of <math data-latex=\"m\"><semantics><mi>m<\/mi><annotation encoding=\"application\/x-tex\">m<\/annotation><\/semantics><\/math> on the other scale.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"668\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/lgeyz2RN.jpeg?resize=1024%2C668&#038;ssl=1\" alt=\"\" class=\"wp-image-9876\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/lgeyz2RN.jpeg?resize=1024%2C668&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/lgeyz2RN.jpeg?resize=300%2C196&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/lgeyz2RN.jpeg?resize=768%2C501&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/lgeyz2RN.jpeg?resize=1536%2C1001&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/lgeyz2RN.jpeg?w=1600&amp;ssl=1 1600w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">How <math data-latex=\"q\"><semantics><mi>q<\/mi><annotation encoding=\"application\/x-tex\">q<\/annotation><\/semantics><\/math> is found using <math data-latex=\"\\text{lerp()}\"><semantics><mtext>lerp()<\/mtext><annotation encoding=\"application\/x-tex\">\\text{lerp()}<\/annotation><\/semantics><\/math><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">So for any <math data-latex=\"m\"><semantics><mi>m<\/mi><annotation encoding=\"application\/x-tex\">m<\/annotation><\/semantics><\/math> in its range, <math data-latex=\"p''_i\"><semantics><msubsup><mi>p<\/mi><mi>i<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/mrow><\/msubsup><annotation encoding=\"application\/x-tex\">p&#8221;_i<\/annotation><\/semantics><\/math> becomes<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msubsup><mi>p<\/mi><mi>i<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/mrow><\/msubsup><mo>=<\/mo><mtext>lerp<\/mtext><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>x<\/mi><mo>+<\/mo><mfrac><mi>u<\/mi><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mi>y<\/mi><mo>+<\/mo><mfrac><mi>v<\/mi><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mi>m<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>\u2212<\/mo><mfrac><mi>i<\/mi><mi>k<\/mi><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">p&#8221;_i = \\text{lerp}(x + \\frac{u}{k},\\ y + \\frac{v}{k},\\ m) &#8211; \\frac{i}{k}<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">But since we do not want the out of range animation progress values on either side, we will apply <math data-latex=\"\\text{clamp}()\"><semantics><mrow><mtext>clamp<\/mtext><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\text{clamp}()<\/annotation><\/semantics><\/math> (the same <code><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/Reference\/Values\/clamp\">clamp()<\/a><\/code> function from CSS) to the <math data-latex=\"p''_i\"><semantics><msubsup><mi>p<\/mi><mi>i<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/mrow><\/msubsup><annotation encoding=\"application\/x-tex\">p&#8221;_i<\/annotation><\/semantics><\/math> values and obtain our final formula!<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>p<\/mi><mi>i<\/mi><\/msub><mo>=<\/mo><mtext>clamp<\/mtext><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mn>0<\/mn><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mtext>lerp<\/mtext><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>x<\/mi><mo>+<\/mo><mfrac><mi>u<\/mi><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mi>y<\/mi><mo>+<\/mo><mfrac><mi>v<\/mi><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mi>m<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>\u2212<\/mo><mfrac><mi>i<\/mi><mi>k<\/mi><\/mfrac><mo separator=\"true\">,<\/mo><mtext>&nbsp;<\/mtext><mn>1<\/mn><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">p_i = \\text{clamp}(0,\\ \\text{lerp}(x + \\frac{u}{k},\\ y + \\frac{v}{k},\\ m) &#8211; \\frac{i}{k},\\ 1)<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Finally, it&#8217;s Time to Animate with CSS<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">By now, you not only know the stagger formula, but also have a deep understanding of how it works. We can easily port it to CSS in a reusable way using the awesome new <code>@function<\/code> feature:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@function<\/span> --stagger(\n  --m,\n  <span class=\"hljs-attribute\">--k:<\/span> <span class=\"hljs-number\">5<\/span>,\n  <span class=\"hljs-attribute\">--i:<\/span> sibling-index(),\n  <span class=\"hljs-attribute\">--u:<\/span> <span class=\"hljs-number\">1<\/span>,\n  <span class=\"hljs-attribute\">--x:<\/span> <span class=\"hljs-number\">0<\/span>,\n  <span class=\"hljs-attribute\">--v:<\/span> sibling-count(),\n  <span class=\"hljs-attribute\">--y:<\/span> <span class=\"hljs-number\">1<\/span>\n) {\n  <span class=\"hljs-selector-tag\">--q1<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--x<\/span>) + <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--u<\/span>) \/ <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--k<\/span>));\n  <span class=\"hljs-selector-tag\">--q2<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--y<\/span>) + <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--v<\/span>) \/ <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--k<\/span>));\n  <span class=\"hljs-selector-tag\">result<\/span>: <span class=\"hljs-selector-tag\">clamp<\/span>(\n    0,\n    <span class=\"hljs-selector-tag\">--lerp<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--q1<\/span>), <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--q2<\/span>), <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--m<\/span>)) <span class=\"hljs-selector-tag\">-<\/span> <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--i<\/span>) \/ <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--k<\/span>),\n    1\n  );\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Note that we can use function calls for default values. These functions will be evaluated when <code>--stagger()<\/code> is called.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Implementing <code>--lerp()<\/code> is easy. For our purposes, we can define it like below:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@function<\/span> --lerp(--a, --b, --x) {\n  <span class=\"hljs-selector-tag\">result<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--a<\/span>) + (<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--b<\/span>) <span class=\"hljs-selector-tag\">-<\/span> <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--a<\/span>)) * <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--x<\/span>));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Above, we assume <code>--x<\/code> is in <math data-latex=\"[0, 1]\"><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">[<\/mo><mn>0<\/mn><mo separator=\"true\">,<\/mo><mn>1<\/mn><mo form=\"postfix\" stretchy=\"false\">]<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">[0, 1]<\/annotation><\/semantics><\/math> range.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now we can animate a custom property <code>--m<\/code> to scrub our animation in an automated way:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@property<\/span> --m {\n  <span class=\"hljs-selector-tag\">syntax<\/span>: \"&lt;<span class=\"hljs-selector-tag\">number<\/span>&gt;\";\n  <span class=\"hljs-selector-tag\">inherits<\/span>: <span class=\"hljs-selector-tag\">true<\/span>;\n  <span class=\"hljs-selector-tag\">initial-value<\/span>: 0;\n}\n\n<span class=\"hljs-keyword\">@keyframes<\/span> stagger-frames {\n  <span class=\"hljs-selector-tag\">to<\/span> {\n    <span class=\"hljs-attribute\">--m<\/span>: <span class=\"hljs-number\">1<\/span>;\n  }\n}\n\n<span class=\"hljs-selector-class\">.parent<\/span> {\n  <span class=\"hljs-attribute\">animation<\/span>: stagger-frames <span class=\"hljs-number\">2s<\/span> linear alternate infinite;\n}\n\n<span class=\"hljs-selector-class\">.child<\/span> {\n  <span class=\"hljs-attribute\">--p<\/span>: <span class=\"hljs-built_in\">--stagger<\/span>(\n    var(--m),\n    <span class=\"hljs-built_in\">var<\/span>(--k),\n    <span class=\"hljs-built_in\">sibling-index<\/span>(),\n    <span class=\"hljs-built_in\">var<\/span>(--u),\n    <span class=\"hljs-built_in\">var<\/span>(--x),\n    <span class=\"hljs-built_in\">var<\/span>(--v),\n    <span class=\"hljs-built_in\">var<\/span>(--y)\n  );\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-built_in\">calc<\/span>(var(--p) * <span class=\"hljs-number\">100%<\/span>);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Note that we also set <code>--k<\/code>, <code>--u<\/code>, <code>--x<\/code>, <code>--v<\/code>, and <code>--y<\/code> without declaring them in CSS. We do this so we can define them on the parent <code>&lt;div&gt;<\/code> via <a href=\"https:\/\/codepen.github.io\/slideVars\/\">slideVars<\/a> and easily experiment with their values.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_LEbReWo\" src=\"\/\/codepen.io\/anon\/embed\/LEbReWo?height=450&amp;theme-id=1&amp;slug-hash=LEbReWo&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed LEbReWo\" title=\"CodePen Embed LEbReWo\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe loading=\"lazy\" title=\"VideoPress Video Player\" aria-label=\"VideoPress Video Player\" width=\"500\" height=\"281\" src=\"https:\/\/videopress.com\/embed\/kLjtvWLc?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0\" frameborder=\"0\" allowfullscreen data-resize-to-parent=\"true\" allow=\"clipboard-write\"><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1770107250'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p class=\"wp-block-paragraph\">It&#8217;s working, but it feels a bit mechanical. To add more life to the animation, we can apply an easing function on top of <code>--p<\/code>. Unfortunately, we can&#8217;t use easing keywords or <code>cubic-bezier()<\/code> or <code>linear()<\/code> functions here. We are left on our own to create easing functions. I&#8217;m not sure if it&#8217;s possible to implement <code>cubic-bezier()<\/code> or <code>linear()<\/code> using <code>@function<\/code>. But we can recreate the ones from <a href=\"https:\/\/easings.net\/\">easings.net<\/a>. For example, here is the CSS version of <em>easeInOutQuad<\/em> using <code>@function<\/code> and <code>if()<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">@<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> --<span class=\"hljs-title\">easeInOutQuad<\/span>(<span class=\"hljs-params\">--x &lt;number&gt;<\/span>) <span class=\"hljs-title\">returns<\/span> &lt;<span class=\"hljs-title\">number<\/span>&gt; <\/span>{\n  <span class=\"hljs-attr\">result<\/span>: <span class=\"hljs-keyword\">if<\/span>(\n    style(--x: max(<span class=\"hljs-keyword\">var<\/span>(--x), <span class=\"hljs-number\">0.5<\/span>)): calc(<span class=\"hljs-number\">1<\/span> - (pow(<span class=\"hljs-number\">-2<\/span> * <span class=\"hljs-keyword\">var<\/span>(--x) + <span class=\"hljs-number\">2<\/span>, <span class=\"hljs-number\">2<\/span>) \/ <span class=\"hljs-number\">2<\/span>));\n    <span class=\"hljs-keyword\">else<\/span>: calc(<span class=\"hljs-number\">2<\/span> * <span class=\"hljs-keyword\">var<\/span>(--x) * <span class=\"hljs-keyword\">var<\/span>(--x));\n  );\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Note the type of <code>--x<\/code> is needed here for <code>if()<\/code> to work correctly. While it works fine without specifying the return type, I&#8217;m writing it out because we\u2019re in the process of specifying types. Also note the clever use of <code>max<\/code>. It&#8217;s not yet possible to use comparison operators there. Here is a comparison with and without this easing function:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.child<\/span> {\n  <span class=\"hljs-attribute\">--p<\/span>: <span class=\"hljs-built_in\">--stagger<\/span>(\n    var(--m),\n    <span class=\"hljs-built_in\">var<\/span>(--k),\n    <span class=\"hljs-built_in\">sibling-index<\/span>(),\n    <span class=\"hljs-built_in\">var<\/span>(--u),\n    <span class=\"hljs-built_in\">var<\/span>(--x),\n    <span class=\"hljs-built_in\">var<\/span>(--v),\n    <span class=\"hljs-built_in\">var<\/span>(--y)\n  );\n}\n\n<span class=\"hljs-selector-class\">.parent<\/span><span class=\"hljs-selector-class\">.one<\/span> <span class=\"hljs-selector-class\">.child<\/span> {\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-built_in\">calc<\/span>(var(--p) * <span class=\"hljs-number\">100%<\/span>);\n}\n\n<span class=\"hljs-selector-class\">.parent<\/span><span class=\"hljs-selector-class\">.two<\/span> <span class=\"hljs-selector-class\">.child<\/span> {\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-built_in\">calc<\/span>(--easeInOutQuad(var(--p)) * <span class=\"hljs-number\">100%<\/span>);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_XJNjVey\" src=\"\/\/codepen.io\/anon\/embed\/XJNjVey?height=450&amp;theme-id=1&amp;slug-hash=XJNjVey&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed XJNjVey\" title=\"CodePen Embed XJNjVey\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe loading=\"lazy\" title=\"VideoPress Video Player\" aria-label=\"VideoPress Video Player\" width=\"500\" height=\"281\" src=\"https:\/\/videopress.com\/embed\/Y8MT5ynN?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0\" frameborder=\"0\" allowfullscreen data-resize-to-parent=\"true\" allow=\"clipboard-write\"><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1770107250'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p class=\"wp-block-paragraph\">You can even take it further by applying a non-linear <code>animation-timing-function<\/code> to the entire animation. For example, using <code>steps()<\/code> lets you create a stop-motion effect:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.parent<\/span><span class=\"hljs-selector-class\">.three<\/span> {\n  <span class=\"hljs-attribute\">animation<\/span>: stagger-frames <span class=\"hljs-number\">2s<\/span> <span class=\"hljs-built_in\">steps<\/span>(<span class=\"hljs-number\">15<\/span>, jump-none) alternate infinite;\n}\n\n<span class=\"hljs-selector-class\">.child<\/span> {\n  <span class=\"hljs-attribute\">--p<\/span>: <span class=\"hljs-built_in\">--stagger<\/span>(\n    var(--m),\n    <span class=\"hljs-built_in\">var<\/span>(--k),\n    <span class=\"hljs-built_in\">sibling-index<\/span>(),\n    <span class=\"hljs-built_in\">var<\/span>(--u),\n    <span class=\"hljs-built_in\">var<\/span>(--x),\n    <span class=\"hljs-built_in\">var<\/span>(--v),\n    <span class=\"hljs-built_in\">var<\/span>(--y)\n  );\n}\n\n<span class=\"hljs-selector-class\">.parent<\/span><span class=\"hljs-selector-class\">.one<\/span> <span class=\"hljs-selector-class\">.child<\/span> {\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-built_in\">calc<\/span>(var(--p) * <span class=\"hljs-number\">100%<\/span>);\n}\n\n<span class=\"hljs-selector-class\">.parent<\/span><span class=\"hljs-selector-class\">.two<\/span> <span class=\"hljs-selector-class\">.child<\/span>,\n<span class=\"hljs-selector-class\">.parent<\/span><span class=\"hljs-selector-class\">.three<\/span> <span class=\"hljs-selector-class\">.child<\/span> {\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-built_in\">calc<\/span>(--easeInOutQuad(var(--p)) * <span class=\"hljs-number\">100%<\/span>);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_ByQLJJW\" src=\"\/\/codepen.io\/anon\/embed\/ByQLJJW?height=450&amp;theme-id=1&amp;slug-hash=ByQLJJW&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed ByQLJJW\" title=\"CodePen Embed ByQLJJW\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe loading=\"lazy\" title=\"VideoPress Video Player\" aria-label=\"VideoPress Video Player\" width=\"500\" height=\"281\" src=\"https:\/\/videopress.com\/embed\/Xw6Pw3ym?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0\" frameborder=\"0\" allowfullscreen data-resize-to-parent=\"true\" allow=\"clipboard-write\"><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1770107250'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p class=\"wp-block-paragraph\">With the following function, we can tweak the output of <code>--stagger()<\/code> or even <code>--easeInOutQuad()<\/code> to create a smooth bump (I found it with the help of AI. AI can be really helpful in figuring out such functions):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@function<\/span> --smoothBump(--x &lt;number&gt;) returns &lt;number&gt; {\n  <span class=\"hljs-selector-tag\">result<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>((1 <span class=\"hljs-selector-tag\">-<\/span> <span class=\"hljs-selector-tag\">cos<\/span>(2 * <span class=\"hljs-selector-tag\">pi<\/span> * <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--x<\/span>))) \/ 2);\n}\n\n<span class=\"hljs-selector-class\">.parent<\/span><span class=\"hljs-selector-class\">.four<\/span> <span class=\"hljs-selector-class\">.child<\/span> {\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-built_in\">calc<\/span>(--smoothBump(--easeInOutQuad(var(--p))) * <span class=\"hljs-number\">100%<\/span>);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_wBozpme\" src=\"\/\/codepen.io\/anon\/embed\/wBozpme?height=550&amp;theme-id=1&amp;slug-hash=wBozpme&amp;default-tab=result\" height=\"550\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed wBozpme\" title=\"CodePen Embed wBozpme\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe loading=\"lazy\" title=\"VideoPress Video Player\" aria-label=\"VideoPress Video Player\" width=\"500\" height=\"281\" src=\"https:\/\/videopress.com\/embed\/Q1mX8pLN?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0\" frameborder=\"0\" allowfullscreen data-resize-to-parent=\"true\" allow=\"clipboard-write\"><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1770107250'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p class=\"wp-block-paragraph\">For some more fun, let&#8217;s connect our animation to scroll. It&#8217;s now easier than ever. With just two lines of code, you can link the animation directly to the user&#8217;s scroll position.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.parent<\/span> {\n  <span class=\"hljs-attribute\">animation<\/span>: stagger-frames;\n  <span class=\"hljs-attribute\">animation-timeline<\/span>: <span class=\"hljs-built_in\">scroll<\/span>();\n}\n\n<span class=\"hljs-selector-class\">.parent<\/span><span class=\"hljs-selector-class\">.three<\/span> {\n  <span class=\"hljs-attribute\">animation<\/span>: stagger-frames <span class=\"hljs-built_in\">steps<\/span>(<span class=\"hljs-number\">15<\/span>, jump-none);\n  <span class=\"hljs-attribute\">animation-timeline<\/span>: <span class=\"hljs-built_in\">scroll<\/span>();\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_azBmEjr\" src=\"\/\/codepen.io\/anon\/embed\/azBmEjr?height=500&amp;theme-id=1&amp;slug-hash=azBmEjr&amp;default-tab=result\" height=\"500\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed azBmEjr\" title=\"CodePen Embed azBmEjr\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe loading=\"lazy\" title=\"VideoPress Video Player\" aria-label=\"VideoPress Video Player\" width=\"500\" height=\"281\" src=\"https:\/\/videopress.com\/embed\/lc7rCEko?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0\" frameborder=\"0\" allowfullscreen data-resize-to-parent=\"true\" allow=\"clipboard-write\"><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1770107250'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s finish with a more interesting example. If you scroll all the way down this page, you&#8217;ll see a cool animation of a number appearing that represents the donation amount from Frontend Masters to open source projects.<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe loading=\"lazy\" title=\"VideoPress Video Player\" aria-label=\"VideoPress Video Player\" width=\"500\" height=\"281\" src=\"https:\/\/videopress.com\/embed\/YTlcCecT?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0\" frameborder=\"0\" allowfullscreen data-resize-to-parent=\"true\" allow=\"clipboard-write\"><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1770107250'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p class=\"wp-block-paragraph\">This is one of those ideal scenarios where the stagger formula shines. It allows you to make effects like below:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_KwNQpBY\" src=\"\/\/codepen.io\/anon\/embed\/KwNQpBY?height=450&amp;theme-id=1&amp;slug-hash=KwNQpBY&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed KwNQpBY\" title=\"CodePen Embed KwNQpBY\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe loading=\"lazy\" title=\"VideoPress Video Player\" aria-label=\"VideoPress Video Player\" width=\"500\" height=\"281\" src=\"https:\/\/videopress.com\/embed\/VgPZbyYq?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0\" frameborder=\"0\" allowfullscreen data-resize-to-parent=\"true\" allow=\"clipboard-write\"><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1770107250'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">That\u2019s it for this article. By taking time out of the equation and thinking in terms of progress-value relationships, we made scrubbable staggered animation possible. I found this core idea in a <a href=\"https:\/\/www.youtube.com\/watch?v=CrsAfgfmgzY\" target=\"_blank\" rel=\"noreferrer noopener\">Blender tutorial<\/a> and fell in love with the technique. Thanks to Filip, the creator of the tutorial. Finally thanks to CSS <code>@function<\/code> and modern CSS features for making it happen painlessly on the web without any JavaScript.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I\u2019d love to see what you\u2019ll build with it. If you make something, share it in the comments below.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here&#8217;s a brand new approach to creating staggered animations in CSS using a single progress value, allowing for smooth linkage to various inputs like scrolling. By utilizing a mathematical formula, it enhances control over animated elements without isolating their timelines, making animations more versatile and scrubbable.<\/p>\n","protected":false},"author":51,"featured_media":9881,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[395,495,7,396,250],"class_list":["post-9759","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-function","tag-clamp","tag-css","tag-if","tag-sibling-index"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/stagger.jpg?fit=2000%2C1200&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9759","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/51"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=9759"}],"version-history":[{"count":50,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9759\/revisions"}],"predecessor-version":[{"id":10035,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9759\/revisions\/10035"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/9881"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=9759"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=9759"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=9759"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}