sibling-index()とsibling-count()で並び順をCSSの計算に持ち込む
sibling-index()とsibling-count()は、要素が親の中で何番目かと、兄弟要素がいくつあるかを整数で返すCSSの関数です。Baseline 2026で主要ブラウザに揃い、順番に応じたアニメーションの遅延や項目数から逆算するレイアウトを、番号を配らずにCSSだけで書けるようになりました。
- Chrome138
- Edge138
- Firefox154
- Safari26.2
はじめに
リストの項目を少しずつずらしてフェードインさせたいとき、これまでは各要素にstyle="--i: 3"のような番号を配るのが定番でした。テンプレート側でインデックスを埋め込むか、li:nth-child(1)から順に、項目数のぶんセレクタを書き並べるかのどちらかです。
sibling-index()とsibling-count()は、この番号をCSS側から取れるようにする関数です。Chromeでは先行して使えていましたが、Safari 26.2に続いてFirefox 154が対応したことでBaseline 2026に加わりました。
2つの関数
sibling-index()は、親の中でその要素が何番目かを返します。:nth-child()と同じく1始まりです。sibling-count()は、自分自身を含めた兄弟要素の総数を返します。Element.childrenのlengthと同じ値です。
どちらも引数を取らず、返すのは<integer>です。整数値なのでcalc()の中で他の値と掛け合わせられますし、整数を受け取るプロパティならcalc()で包まずそのまま書けます。
li {
order: sibling-index();
1番目の要素なら1
width: calc(100% / sibling-count());
兄弟が5つなら20%ずつ
}何を数えるか
数えるのは要素だけです。テキストノードやコメントノードは無視されるので、HTMLの改行やインデントで数がずれることはありません。
一方で、要素であれば種類は問われません。リストの中に<style>や<script>を置くと、それも1つの兄弟として数えられて後続の番号がずれます。番号に影響させたくない要素は親の外に出しておきます。
display: noneの兄弟も数に入ります。数えているのはDOMツリー上の位置であって、レンダリングされるかどうかは関係ありません。CSSの多くの処理は、Shadow DOMのslotに振り分けたあとの木であるflat treeを対象にします。この2つは:nth-child()などと同じくDOMツリーのほうを数える、と仕様に注記されています。
要素が増えたり減ったりすると値は計算し直されます。リストの先頭に要素を差し込めば、後続の要素のsibling-index()はすべて1つずつ繰り上がります。
何が番号を消費するか
style、script、display:noneの項目を含むリストに、sibling-index()の値を表示しています。画面に出ない要素も番号を消費するので、見えている番号が飛びます。
見えているものだけだと番号は2、3、5、7と飛びます。スイッチを入れると、1の<style>、4のdisplay: noneな項目、6の<script>が現れて番号がつながります。コメントとテキストノードは番号を消費しません。
順番に応じて遅延をずらす
もっとも素直な使いどころは、並び順に応じたアニメーションの遅延です。
li {
animation: fade-in 320ms backwards;
animation-delay: calc(sibling-index() * 90ms);
}これまで同じことをするには、テンプレート側で番号を出力しておく必要がありました。
<li style="--i: 1">項目</li>
<li style="--i: 2">項目</li>
<li style="--i: 3">項目</li>sibling-index()があれば、この--iは要らなくなります。項目が増減しても、マークアップ側で番号を振り直す処理は不要です。
sibling-index()で順番にフェードインさせる
animation-delayをsibling-index()から計算しています。項目数を変えても、インラインのstyleで番号を配り直すことなく遅延が付き直します。
遅延はanimation-delay: calc(sibling-index() * 90ms)、行頭の番号はcounter-resetに渡したsibling-index()です。項目数を変えても、マークアップ側で番号を配り直す必要はありません。
兄弟の数から幅を決める
sibling-count()は、項目数が動的に決まるレイアウトに向いています。タブやツールバーのように、子を横に等分したいケースです。
.tabs > * {
width: calc(100% / sibling-count());
}sibling-count()が数えるのは、常にその関数を書いた要素の兄弟です。親側に書いたgrid-template-columns: repeat(sibling-count(), 1fr)は、子の数ではなく親自身の兄弟の数で列を作ってしまいます。子の数から列を決めたい用途には使えません。
.tabs {
grid-template-columns: repeat(sibling-count(), 1fr);
.tabs の子ではなく .tabs 自身の兄弟の数になる
}数えたいのが子であれば、calc()は子側に書きます。
sibling-count()とsibling-index()に追従するバー
幅をsibling-count()で等分し、高さをsibling-index()から決めています。先頭に項目を追加すると、後続の番号が繰り上がって高さが変わります。
幅はcalc(100% / sibling-count())、高さは@propertyで登録した--scid-bar-indexにsibling-index()を入れて内側のバーへ渡しています。ラベルの数字もcounter-resetに渡したsibling-index()です。先頭に追加すると後続の番号が繰り上がり、高さがそのまま追従します。
カスタムプロパティ経由で子孫に渡す
計算結果をカスタムプロパティに逃がして子孫で使い回したくなりますが、@propertyで登録していないカスタムプロパティでは意図した値になりません。
li {
--i: sibling-index();
}
li span {
width: calc(var(--i) * 10px);
li の番号ではなく span 自身の番号で解決される
}登録されていないカスタムプロパティは、値がトークンのまま保持されてvar()で参照した場所に展開されます。そのためsibling-index()が解決されるのはliではなくspanの位置で、spanが唯一の子であればどのliの中でも1になります。
@propertyで登録すると、宣言した要素の計算値の段階で整数に確定してから継承されます。
@property --i {
syntax: '<integer>';
inherits: true;
initial-value: 0;
}
li {
--i: sibling-index();
}
li span {
width: calc(var(--i) * 10px);
li の番号がそのまま降りてくる
}同じ要素の中で使い切るなら登録していないカスタムプロパティでも結果は変わりません。子孫に値を渡したいときだけ、@propertyでの登録が必要になります。
おわりに
sibling-index()とsibling-count()は、これまでテンプレート側で埋め込んでいた並び順の情報をCSSの値として扱えるようにする関数です。Baseline 2026で主要ブラウザに揃ったので、インラインのstyleで番号を配っていた箇所を減らしていけます。仕様には:nth-child()のようにofで数える対象を絞る引数を将来追加しうるという注記もあり、兄弟の一部だけを数える使い方はこれからの拡張に委ねられています。