{"id":7631,"date":"2025-11-07T10:08:18","date_gmt":"2025-11-07T15:08:18","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=7631"},"modified":"2025-11-07T10:08:19","modified_gmt":"2025-11-07T15:08:19","slug":"staggered-animation-with-css-sibling-functions","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/staggered-animation-with-css-sibling-functions\/","title":{"rendered":"Staggered Animation with CSS sibling-* Functions"},"content":{"rendered":"\n<p>The CSS functions <code>sibling-index()<\/code> and <code>sibling-count()<\/code> return an element\u2019s position relative to its siblings and the total number of siblings, including itself. This is useful for styling elements based on their positions. <\/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\">\"parent\"<\/span>&gt;<\/span> <span class=\"hljs-comment\">&lt;!-- sibling-count() = 3 --&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"child\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span> <span class=\"hljs-comment\">&lt;!-- sibling-index() = 1 --&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"child\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span> <span class=\"hljs-comment\">&lt;!-- sibling-index() = 2 --&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"child\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span> <span class=\"hljs-comment\">&lt;!-- sibling-index() = 3 --&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<p>For instance, to create a pyramid chart, we could proportionally increase the widths of elements as they align.<\/p>\n\n\n\n<p>The integers returned by <code>sibling-index()<\/code> and <code>sibling-count()<\/code> can be easily computed with other data types like length, angle, and time. An incremental or decremental time sequence is the foundation of any staggered animation where elements animate consecutively. For example:<\/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\">.el<\/span> {\n  <span class=\"hljs-attribute\">animation-delay<\/span>: <span class=\"hljs-built_in\">calc<\/span>(sibling-index() * <span class=\"hljs-number\">0.1s<\/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>This post covers a demo where selecting an item causes the preceding and succeeding items to disappear sequentially from the outside.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_EaPREMe\" src=\"\/\/codepen.io\/anon\/embed\/EaPREMe?height=450&amp;theme-id=1&amp;slug-hash=EaPREMe&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed EaPREMe\" title=\"CodePen Embed EaPREMe\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-layout\">The Layout<\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" 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\">main<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"cards-wrapper\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"cards\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"checkbox\"<\/span> <span class=\"hljs-attr\">aria-label<\/span>=<span class=\"hljs-string\">\"movie, only yesterday\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"cards\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"checkbox\"<\/span> <span class=\"hljs-attr\">aria-label<\/span>=<span class=\"hljs-string\">\"movie, the wind rises\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"cards\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"checkbox\"<\/span> <span class=\"hljs-attr\">aria-label<\/span>=<span class=\"hljs-string\">\"movie, howl's moving castle\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"cards\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"checkbox\"<\/span> <span class=\"hljs-attr\">aria-label<\/span>=<span class=\"hljs-string\">\"movie, ponyo\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"cards\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"checkbox\"<\/span> <span class=\"hljs-attr\">aria-label<\/span>=<span class=\"hljs-string\">\"movie, the cat returns\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><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<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\">.cards-wrapper<\/span> {\n  <span class=\"hljs-attribute\">display<\/span>: flex;\n  <span class=\"hljs-attribute\">gap<\/span>: <span class=\"hljs-number\">10px<\/span>;\n  <span class=\"hljs-attribute\">justify-content<\/span>: center;\n}\n\n<span class=\"hljs-selector-class\">.cards<\/span> {\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">15vw<\/span>;\n  <span class=\"hljs-attribute\">aspect-ratio<\/span>: <span class=\"hljs-number\">3<\/span>\/<span class=\"hljs-number\">4.2<\/span>;\n  <span class=\"hljs-attribute\">contain<\/span>: layout;\n  <span class=\"hljs-comment\">\/* etc. *\/<\/span>\n\n  input&#91;type=\"checkbox\"] {\n    <span class=\"hljs-attribute\">position<\/span>: absolute;\n    <span class=\"hljs-attribute\">inset<\/span>: <span class=\"hljs-number\">0<\/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>This HTML contains a group of <code>.cards<\/code> inside a <code>.cards-wrapper<\/code>, arranged horizontally using flexbox. Each card has a checkbox covering its entire size, triggering selection on click.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-selectors\">The Selectors<\/h2>\n\n\n\n<p>Before seeing what happens when a card is selected, let\u2019s see the different CSS selectors we\u2019ll be using to target the different parts of the user interface.<\/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\">.cards<\/span><span class=\"hljs-selector-pseudo\">:has(<\/span><span class=\"hljs-selector-pseudo\">:checked)<\/span> {\n\n  <span class=\"hljs-comment\">\/* Style rules for the chosen card (the one with a checked box).\n    Highlight the chosen card. *\/<\/span>   \n\n  <span class=\"hljs-attribute\">.cards-wrapper<\/span>:<span class=\"hljs-built_in\">has<\/span>(:checked) .cards:<span class=\"hljs-built_in\">not<\/span>(&amp;) {\n    <span class=\"hljs-comment\">\/* Style rules for the remaining cards when one is chosen.  \n      Animate the unchosen cards to disappear. *\/<\/span> \n  }\n\n  &amp; ~ <span class=\"hljs-selector-class\">.cards<\/span> {\n    <span class=\"hljs-comment\">\/* Style rules for cards to the right-side of the chosen one. \n       Set a decremental delay time for the disappearance. *\/<\/span>\n\n    <span class=\"hljs-attribute\">.cards<\/span>:<span class=\"hljs-built_in\">not<\/span>(&amp;) {\n       <span class=\"hljs-comment\">\/* Style rules for cards that aren't to the right of the chosen one. \n          Set an incremental delay time. *\/<\/span>\n    }\n  } \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<h2 class=\"wp-block-heading\" id=\"the-declarations\">The Declarations<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Highlight The Chosen Card<\/h3>\n\n\n\n<p>The selected card gets a grey dashed border:<\/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-tag\">border<\/span>: 2<span class=\"hljs-selector-tag\">px<\/span> <span class=\"hljs-selector-tag\">dashed<\/span> <span class=\"hljs-selector-id\">#888<\/span>;<\/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<h3 class=\"wp-block-heading\">Animate The Unchosen Cards to Disappear<\/h3>\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-selector-tag\">opacity<\/span>: 0;\n<span class=\"hljs-selector-tag\">width<\/span>: 0; \n<span class=\"hljs-selector-tag\">display<\/span>: <span class=\"hljs-selector-tag\">none<\/span>; \n<span class=\"hljs-selector-tag\">transition<\/span>: 0<span class=\"hljs-selector-class\">.3s<\/span> <span class=\"hljs-selector-tag\">calc<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--n<\/span>) * 0<span class=\"hljs-selector-class\">.2s<\/span>) <span class=\"hljs-selector-tag\">all<\/span>;\n<span class=\"hljs-selector-tag\">transition-behavior<\/span>: <span class=\"hljs-selector-tag\">allow-discrete<\/span>;\n\n<span class=\"hljs-selector-tag\">input<\/span> { \n  <span class=\"hljs-attribute\">pointer-events<\/span>: none; \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<ol class=\"wp-block-list\">\n<li>Zero\u00a0<code>opacity<\/code>\u00a0and\u00a0<code>width<\/code>\u00a0create a fade out and horizontal shrinking of the card, including the space the card occupies.<\/li>\n\n\n\n<li><code>transition-behavior: allow-discrete<\/code>\u00a0allows\u00a0<code>display: none<\/code>\u00a0to apply at the <em>end<\/em> of the transition, which is appropriate as we do want to apply that to remove them from the accessibility tree.<\/li>\n\n\n\n<li>The <code>transition<\/code> time, affecting <code>all<\/code> properties that change, is <code>0.3s<\/code>. The transition delay is a multiple of <code>0.2s<\/code> and the CSS variable <code>--n<\/code> (to be discussed)<\/li>\n\n\n\n<li>To prevent unchosen cards from being clicked and chosen while disappearing, their checkbox <code>input<\/code> get <code>pointer-events: none<\/code><\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Decremental Delay Time for Cards On The Right<\/h3>\n\n\n\n<p>The cards, succeeding the chosen one, need descending transition delay times. Thus, <code>--n<\/code> is decremented for each element (left to right) to the right of the chosen card.<\/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-tag\">--n<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(<span class=\"hljs-selector-tag\">sibling-count<\/span>() <span class=\"hljs-selector-tag\">-<\/span> <span class=\"hljs-selector-tag\">sibling-index<\/span>() + 1);<\/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<p>Let\u2019s say the third card is chosen. The remaining cards (4 to last) have to vanish in reverse order. The math below shows each of those cards\u2019 <code>--n<\/code> value and delay time (<code>--n<\/code> \u00d7 0.2s).<\/p>\n\n\n\n<p>Our example has five cards, so <code>sibling-count()<\/code> is 5. Let&#8217;s see how <code>--n<\/code> calculated for each card after the 3rd (chosen) card:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">4th card<br>--------<br>--n = calc( sibling-count() - sibling-index() + 1 )<br>--n = calc( 5 - 4 + 1 )<br>--n = 2<br><br>delay time = calc( var(--n) * 0.2s )<br>delay time = calc( 2 * 0.2s )<br>delay time = 0.4s<br><br><br>5th card<br>--------<br>--n = calc( sibling-count() - sibling-index() + 1 )<br>--n = calc( 5 - 5 + 1 )<br>--n = 1<br><br>delay time = calc( var(--n) * 0.2s )<br>delay time = calc( 1 * 0.2s )<br>delay time = 0.2s<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Incremental Delay Time for Cards On The Left<\/h3>\n\n\n\n<p>The <code>sibling-index()<\/code> value alone is enough, since the delay time of the elements (left to right) to the left of the chosen card increases outward in. First card goes first, then the second, and so forth.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"> <span class=\"hljs-selector-tag\">--n<\/span>: <span class=\"hljs-selector-tag\">sibling-index<\/span>();<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><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>Here&#8217;s now <code>--n<\/code> calculates and thus makes the delay time for each card <em>before<\/em> the 3rd chosen card: <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">1st card<br>--------<br>--n = sibling-index() <br>--n = 1<br><br>delay time = calc( var(--n) * 0.2s )<br>delay time = calc( 1 * 0.2s )<br>delay time = 0.2s<br><br><br>2nd card<br>--------<br>--n = sibling-index() <br>--n = 2<br><br>delay time = calc( var(--n) * 0.2s )<br>delay time = calc( 2 * 0.2s )<br>delay time = 0.4s<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-rulesets\">The Rulesets<\/h2>\n\n\n\n<p>All combined, these are the style rules that are applied when a card is chosen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-comment\">\/* chosen card *\/<\/span>\n<span class=\"hljs-selector-class\">.cards<\/span><span class=\"hljs-selector-pseudo\">:has(<\/span><span class=\"hljs-selector-pseudo\">:checked)<\/span> {\n\n   <span class=\"hljs-attribute\">border<\/span>: <span class=\"hljs-number\">2px<\/span> dashed <span class=\"hljs-number\">#888<\/span>;\n\n  <span class=\"hljs-comment\">\/* cards that aren't chosen, when one has been *\/<\/span>\n  <span class=\"hljs-attribute\">.cards-wrapper<\/span>:<span class=\"hljs-built_in\">has<\/span>(:checked) .cards:<span class=\"hljs-built_in\">not<\/span>(&amp;) {\n    opacity: <span class=\"hljs-number\">0<\/span>;\n    <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">0<\/span>; \n    <span class=\"hljs-attribute\">display<\/span>: none; \n    <span class=\"hljs-attribute\">transition<\/span>: .<span class=\"hljs-number\">3s<\/span> <span class=\"hljs-built_in\">calc<\/span>(var(--n) * .<span class=\"hljs-number\">2s<\/span>) all;\n    <span class=\"hljs-attribute\">transition-behavior<\/span>: allow-discrete;\n    input { <span class=\"hljs-attribute\">pointer-events<\/span>: none; }\n  }\n\n  <span class=\"hljs-comment\">\/* cards after the chosen one *\/<\/span>\n  &amp; ~ <span class=\"hljs-selector-class\">.cards<\/span>{\n    <span class=\"hljs-attribute\">--n<\/span>: <span class=\"hljs-built_in\">calc<\/span>(sibling-count() - <span class=\"hljs-built_in\">sibling-index<\/span>() + <span class=\"hljs-number\">1<\/span>);\n    <span class=\"hljs-comment\">\/* cards not after the chosen one *\/<\/span>\n    <span class=\"hljs-attribute\">.cards<\/span>:<span class=\"hljs-built_in\">not<\/span>(&amp;){\n      --n: <span class=\"hljs-built_in\">sibling-index<\/span>();\n    }\n  } \n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><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=\"learn-more\">Note: Among the \u201ccards not after the chosen one\u201d (see above snippet) the chosen card is also included, but since it doesn\u2019t have the\u00a0<code>transition<\/code>\u00a0declaration, there\u2019s no effect. If you want, you can exclude it by adding\u00a0<code>:not(:has(:checked))<\/code>\u00a0to the selector, but it\u2019s not necessary in our example.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-fallback\">The Fallback<\/h2>\n\n\n\n<p>If a browser doesn\u2019t support <code>sibling-*<\/code> functions, we can calculate <code>--n<\/code> in JavaScript to determine the elements\u2019 positions among their siblings.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">if<\/span>(!CSS.supports(<span class=\"hljs-string\">'order'<\/span>, <span class=\"hljs-string\">'sibling-index()'<\/span>)) {\n\n  <span class=\"hljs-comment\">\/\/ Turn the NodeList to an array for easier manipulation<\/span>\n  <span class=\"hljs-keyword\">const<\/span> CARDS = <span class=\"hljs-built_in\">Array<\/span>.from(<span class=\"hljs-built_in\">document<\/span>.querySelectorAll(<span class=\"hljs-string\">'.cards'<\/span>));\n\n  <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">'.cards-wrapper'<\/span>).addEventListener(<span class=\"hljs-string\">'change'<\/span>, (e) =&gt; {\n\n    <span class=\"hljs-comment\">\/\/ Index of the card with the checkbox that fired the 'change' event.<\/span>\n    <span class=\"hljs-keyword\">const<\/span> IDX = CARDS.indexOf(e.target.parentElement);\n\n    <span class=\"hljs-comment\">\/\/ All cards after the chosen one. If there are three,<\/span>\n    <span class=\"hljs-comment\">\/\/ they get --n values of 3, 2, 1<\/span>\n    CARDS.slice(IDX + <span class=\"hljs-number\">1<\/span>).forEach(<span class=\"hljs-function\">(<span class=\"hljs-params\">card, idx, arr<\/span>) \n      =&gt;<\/span> card.style.setProperty(<span class=\"hljs-string\">'--n'<\/span>, <span class=\"hljs-string\">`<span class=\"hljs-subst\">${arr.length - idx}<\/span>`<\/span>));\n\n    <span class=\"hljs-comment\">\/\/ All cards up to the chosen one. If there are three,<\/span>\n    <span class=\"hljs-comment\">\/\/ they get --n values of 1, 2, 3<\/span>\n    CARDS.slice(<span class=\"hljs-number\">0<\/span>, IDX).forEach(<span class=\"hljs-function\">(<span class=\"hljs-params\">card, idx<\/span>) \n       =&gt;<\/span> card.style.setProperty(<span class=\"hljs-string\">'--n'<\/span>, <span class=\"hljs-string\">`<span class=\"hljs-subst\">${idx + <span class=\"hljs-number\">1<\/span>}<\/span>`<\/span>));\n  });\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><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>Below is another example, where you\u2019ll be able see an accordion sort of animation by using the <code>sibling-index()<\/code> function to show and hide the items, even without transition delay, but with the delay the staggered effect comes through.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_PwZBLoy\" src=\"\/\/codepen.io\/anon\/embed\/PwZBLoy?height=450&amp;theme-id=1&amp;slug-hash=PwZBLoy&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed PwZBLoy\" title=\"CodePen Embed PwZBLoy\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n","protected":false},"excerpt":{"rendered":"<p>The new CSS sibling-index() (and -count()) functions are perfect for staggered timing affects. This goes a little step further staggering both before and after a selected element.<\/p>\n","protected":false},"author":20,"featured_media":7723,"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,250,389],"class_list":["post-7631","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-css","tag-sibling-index","tag-transition"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/11\/Staggered-Animation-with-CSS-sibling-_-Functions-1.jpg?fit=1140%2C676&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/7631","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\/20"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=7631"}],"version-history":[{"count":9,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/7631\/revisions"}],"predecessor-version":[{"id":7704,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/7631\/revisions\/7704"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/7723"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=7631"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=7631"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=7631"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}