{"id":7338,"date":"2025-10-07T14:40:28","date_gmt":"2025-10-07T19:40:28","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=7338"},"modified":"2025-10-07T14:40:28","modified_gmt":"2025-10-07T19:40:28","slug":"numbers-that-fall-scroll-driven-animations-sibling-index","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/numbers-that-fall-scroll-driven-animations-sibling-index\/","title":{"rendered":"Numbers That Fall (Scroll-Driven Animations &amp; Sibling Index)"},"content":{"rendered":"\n<p>Maybe you noticed the big number at the bottom of the current design of this site? As I write, that number is $839,000 and it&#8217;s just a reminder of how Frontend Masters gives back to the open source community. I did that design so I figured I&#8217;d write down how it works here.<\/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 title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='335' src='https:\/\/videopress.com\/embed\/zXCXu2Xi?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=1739540970'><\/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\">Figuring out the Scroll-Driven Animation<\/h2>\n\n\n\n<p>It&#8217;s helpful to just stop and think about how you want the animation to behave in regard to scrolling. In this case, I&#8217;d like the numbers do be <strong>done<em> <\/em><\/strong>animating shortly after they arrive on the page from the bottom. This means they will be visible\/readable <strong>most<em> <\/em><\/strong>of the time, which feels appropriate for text especially. <\/p>\n\n\n\n<p>With this in mind, I recommend heading to Bramus Van Damme&#8217;s tool <a href=\"https:\/\/scroll-driven-animations.style\/tools\/view-timeline\/ranges\/#range-start-name=cover&amp;range-start-percentage=0&amp;range-end-name=entry-crossing&amp;range-end-percentage=120&amp;view-timeline-axis=block&amp;view-timeline-inset=0&amp;subject-size=smaller&amp;subject-animation=fly-in&amp;interactivity=clicktodrag&amp;show-areas=yes&amp;show-fromto=yes&amp;show-labels=yes\">View Progress Timeline: Ranges and Animation Progress Visualizer<\/a>. This tool is <em>extremely <\/em>useful to play around with to understand the different possibilities with different sizes of content. After various playing, I found a good match of <code>animation-range-start<\/code> and <code>animation-range-end<\/code> values for what I had in mind.<\/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 title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='447' src='https:\/\/videopress.com\/embed\/fuIzQEq3?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=1739540970'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>In the video above, we&#8217;re seeing a &#8220;fly in&#8221; animation. But that animation doesn&#8217;t matter. It&#8217;s just showing us the <strong>time range<em> <\/em><\/strong>that will be relevant to whatever animation we choose to apply. Our numbers are going to &#8220;fall in&#8221;, and we&#8217;ll get to that soon.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Split the Numbers Up<\/h2>\n\n\n\n<p>We&#8217;ll put each number in a <code>&lt;span><\/code> so we can animate them individually. But we&#8217;ll make the accessible text for that component read properly with an <code>aria-label<\/code> attribute:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"dollar-amount\"<\/span> <span class=\"hljs-attr\">aria-label<\/span>=<span class=\"hljs-string\">\"$839,000\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"dollar-sign\"<\/span>&gt;<\/span>$<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>8<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>3<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>9<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>,<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Animate Each Span<\/h2>\n\n\n\n<p>Each one of the numbers will have the same animation:<\/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-selector-class\">.dollar-amount<\/span> {\n  ...\n\n  &gt; span {\n    <span class=\"hljs-attribute\">display<\/span>: inline-block;\n    <span class=\"hljs-attribute\">animation<\/span>: scroll-in linear both;\n    <span class=\"hljs-attribute\">animation-timeline<\/span>: <span class=\"hljs-built_in\">view<\/span>();\n    <span class=\"hljs-attribute\">animation-range<\/span>: cover <span class=\"hljs-number\">0%<\/span> entry-crossing <span class=\"hljs-number\">120%<\/span>;\n  }\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>The animation we&#8217;ve named there <code>scroll-in<\/code> might look like this:<\/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\">@keyframes<\/span> scroll-in {\n  <span class=\"hljs-selector-tag\">from<\/span> {\n    <span class=\"hljs-attribute\">scale<\/span>: <span class=\"hljs-number\">1.33<\/span>;\n    <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n    <span class=\"hljs-attribute\">translate<\/span>: <span class=\"hljs-number\">0<\/span> -<span class=\"hljs-number\">50px<\/span>\n  }\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>That will make each letter &#8220;fall&#8221; from 50px above down to it&#8217;s natural position, while also fading in and starting bigger and ending up it&#8217;s intrinsic size. <\/p>\n\n\n\n<p>But, they will all do that the exact same way. We want staggering!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Staggering in a Scroll-Driven World<\/h2>\n\n\n\n<p>Usually animation staggering uses <code>transition-delay<\/code> or <code>animation-delay<\/code> on individual &#8220;nth&#8221; elements. Something like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.dollar-amount<\/span> {\n  <span class=\"hljs-attribute\">span<\/span>:<span class=\"hljs-built_in\">nth-child<\/span>(<span class=\"hljs-number\">1<\/span>) { animation-delay: <span class=\"hljs-number\">0.1s<\/span>; }\n  <span class=\"hljs-selector-tag\">span<\/span><span class=\"hljs-selector-pseudo\">:nth-child(2)<\/span> { <span class=\"hljs-attribute\">animation-delay<\/span>: <span class=\"hljs-number\">0.2s<\/span>; }\n  <span class=\"hljs-selector-tag\">span<\/span><span class=\"hljs-selector-pseudo\">:nth-child(3)<\/span> { <span class=\"hljs-attribute\">animation-delay<\/span>: <span class=\"hljs-number\">0.3s<\/span>; }\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\">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>But that&#8217;s not going to work for us here. Delay in a scroll-driven animation doesn&#8217;t mean anything. I don&#8217;t think anyway?! I tried the above and it didn&#8217;t do anything. <\/p>\n\n\n\n<p>Fortunately, the effect I was going for was a bit different anyway. I wanted the numbers to have a staggered fall in effect (see video above) where subsequent numbers almost look like they are falling from a different height and yet all arrive at the same time. So I could handle that like&#8230;<\/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\">.dollar-amount<\/span> {\n  <span class=\"hljs-attribute\">span<\/span>:<span class=\"hljs-built_in\">nth-child<\/span>(<span class=\"hljs-number\">1<\/span>) { translate: <span class=\"hljs-number\">0<\/span> -<span class=\"hljs-number\">20px<\/span>; }\n  <span class=\"hljs-selector-tag\">span<\/span><span class=\"hljs-selector-pseudo\">:nth-child(2)<\/span> { <span class=\"hljs-attribute\">translate<\/span>: <span class=\"hljs-number\">0<\/span> -<span class=\"hljs-number\">40px<\/span>; }\n  <span class=\"hljs-selector-tag\">span<\/span><span class=\"hljs-selector-pseudo\">:nth-child(3)<\/span> { <span class=\"hljs-attribute\">translate<\/span>: <span class=\"hljs-number\">0<\/span> -<span class=\"hljs-number\">60px<\/span>; }\n  ...\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<p>But, if we&#8217;re being really future-looking, we can handle it 1) within the @keyframes 2) in one line.<\/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-keyword\">@keyframes<\/span> scroll-in {\n  <span class=\"hljs-selector-tag\">from<\/span> {\n    <span class=\"hljs-attribute\">scale<\/span>: <span class=\"hljs-number\">1.33<\/span>;\n    <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n    <span class=\"hljs-attribute\">translate<\/span>: \n      <span class=\"hljs-comment\">\/* x *\/<\/span> <span class=\"hljs-built_in\">calc<\/span>(sibling-index() * <span class=\"hljs-number\">4px<\/span>)\n      <span class=\"hljs-comment\">\/* y *\/<\/span> <span class=\"hljs-built_in\">calc<\/span>(sibling-index() * -<span class=\"hljs-number\">20px<\/span>);\n  }\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<p>The <code>sibling-index()<\/code> function is perfect for staggering of any kind. It&#8217;ll return <code>1<\/code> for what would be <code>:nth-child(1)<\/code>, <code>2<\/code> for what would be <code>:nth-child(2)<\/code>, etc. Then we can use that integer in a calculation or delay. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Demo<\/h2>\n\n\n\n<p>That should do it! <\/p>\n\n\n\n<p>(Note this will only work in Chrome&#8217;n&#8217;friends due to the <code>sibling-index()<\/code> usage. I&#8217;ll leave it as an exercise for the reader to write a fallback that supports a deeper set of browsers.)<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_0199bab6-da71-7fa5-a43c-c71f7d37df93\" src=\"\/\/codepen.io\/editor\/anon\/embed\/0199bab6-da71-7fa5-a43c-c71f7d37df93?height=450&amp;theme-id=1&amp;slug-hash=0199bab6-da71-7fa5-a43c-c71f7d37df93&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed 0199bab6-da71-7fa5-a43c-c71f7d37df93\" title=\"CodePen Embed 0199bab6-da71-7fa5-a43c-c71f7d37df93\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>The part that feels the weirdest to me are the &#8220;magic number&#8221;y values as part of the <code>animation-range<\/code>. But I guess they are about as magic as <code>font-size<\/code> or the like. They are values that describe the animation that works best for you, even if they are a little hard to immediately visualize. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>With view() style scroll-driven animations, it&#8217;s helpful to think about how you want it to work then tweak the ranges toward that goal. <\/p>\n","protected":false},"author":1,"featured_media":7359,"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":[7,57],"class_list":["post-7338","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-css","tag-scroll-driven-animations"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Numbers-That-Fall-Scroll-Driven-Animations-Sibling-Index-1.jpg?fit=1140%2C676&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/7338","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\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=7338"}],"version-history":[{"count":8,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/7338\/revisions"}],"predecessor-version":[{"id":7360,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/7338\/revisions\/7360"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/7359"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=7338"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=7338"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=7338"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}