CSSの sibling-index / sibling-count の使い方。番号・総数を取得して自動計算する最新手法

Web制作において、カード要素に「1枚ずつ異なるアニメーション遅延」を与えたり、「要素の数に応じて色をグラデーション」させたりしたいシーンは多いかと思います。
これまで、こうした動的な演出にはJavaScriptを使うか、少し面倒ではありますが、 nth-child を何行も書く必要がありました。しかし、sibling-index() と sibling-count() といったCSSの関数の登場により、CSS単体で要素の「順番」と「総数」を把握できるようになりました。
ここでは、sibling-index() と sibling-count() を活用方法として、カードレイアウトの構築とアニメーションをサンプルに、その使い方をご紹介します。
以下、主要ブラウザのサポート状況になります。
Can I use( sibling-count() and sibling-index() )
https://caniuse.com/wf-sibling-count
2026年1月末時点では、Firefox以外はサポートされています。
そんなこともありますので、サポートされていないブラウザ用の予備(フォールバック)のコードも含めてご紹介いたします。
まずは、sibling-index()やsibling-count()について理解しましょう。
役割や主な活用例は以下になります。
| 機能 | 役割(返り値) | 主な活用例 |
| sibling-index() | 自分が兄弟の中で何番目かを取得 | スタッガー(時間差)アニメーション、段階的な色変化 |
| sibling-count() | 兄弟要素が全部で何個あるかを取得 | 全体幅の自動均等割、グラデーションの調整 |
sibling-index / sibling-count の使い方
まずは、今回ご紹介するカードレイアウトの構築とアニメーションのサンプルの全コードを共有します。
HTML
<div class="card-container">
<div class="card"><h3>01</h3><p>Strategy</p></div>
<div class="card"><h3>02</h3><p>Design</p></div>
<div class="card"><h3>03</h3><p>Development</p></div>
<div class="card"><h3>04</h3><p>Testing</p></div>
<div class="card"><h3>05</h3><p>Release</p></div>
</div>
CSS
:root {
--bg-color: #f8fafc;
--accent-blue: #007aff;
}
body {
background-color: var(--bg-color);
margin: 0;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-family: sans-serif;
}
.card-container {
display: flex;
flex-direction: row;
gap: 20px;
width: 100%;
max-width: none;
padding: 20px;
}
.card {
/* サイズの固定:中身に関わらず均等に並べる */
flex: 1 1 0;
min-width: 0;
aspect-ratio: 3 / 4;
/* 余白の確保:box-sizingを指定 */
box-sizing: border-box;
padding: 30px;
background: white;
border-radius: 24px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.05);
/* 配置:中身を中央に */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
/* 基本の色:最新機能が動かないブラウザ用の予備(フォールバック) */
border-bottom: 6px solid var(--accent-blue);
transition: all 0.4s cubic-bezier(0.2, 1, 0.2, 1);
opacity: 1;
transform: translateY(0);
}
.card:hover {
transform: translateY(-15px) !important; /* アニメーション終了後もホバーが確実に動くように !important を付与 */
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
z-index: 10;
}
/* sibling-index() を使った色の動的変化とアニメーション */
@supports (animation-delay: calc(sibling-index() * 1s)) {
.card {
/* インデックスに基づいた色に塗り替える */
border-bottom-color: hsl(210, 100%, calc(40% + sibling-index() * 6%));
/* 制御を保持し続けない backwards を設定し、ホバーを阻害しない */
animation: slideUp 0.8s ease backwards;
animation-delay: calc(sibling-index() * 0.1s);
}
}
.card h3 {
font-size: 1.8rem;
margin: 0;
color: #1e293b;
}
.card p {
font-size: 0.9rem;
color: #64748b;
margin-top: 10px;
text-align: center;
line-height: 1.4;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
.card-container {
display: flex;
flex-wrap: wrap;
justify-content: start;
gap: 20px;
}
.card {
flex: 0 0 calc((100% - 20px) / 2); /* (100%幅 - 隙間20px) / 2列 = カード1枚の幅 */
min-width: 0;
aspect-ratio: 1 / 1;
padding: 20px;
box-sizing: border-box;
}
}
@media (max-width: 480px) {
.card {
flex: 1 1 100%;
aspect-ratio: auto;
min-height: 120px;
}
}
それでは、このサンプルのポイントやsibling-index / sibling-count の役割について詳しく解説していきます。
1. HTML:シンプルさが生む「自動化」の土台
このHTMLの最大の特徴は、各カードに番号や特定のクラスを振っていないことです。
<div class="card-container">
<div class="card"><h3>01</h3><p>Strategy</p></div>
<div class="card"><h3>02</h3><p>Design</p></div>
<div class="card"><h3>03</h3><p>Development</p></div>
<div class="card"><h3>04</h3><p>Testing</p></div>
<div class="card"><h3>05</h3><p>Release</p></div>
</div>
通常、カードごとに色や動きを変えるには「card-1」「card-2」といった名前が必要ですが、今回は最新のCSSが「自分が兄弟(siblings)の中で何番目か」を自動で数えてくれるため、このシンプルな構造が維持できます。
2. レイアウト:サイズを揃える
カードのサイズがバラバラにならないよう、.card には非常に精密な計算をさせています。
.card {
flex: 1 1 0;
min-width: 0;
aspect-ratio: 3 / 4;
box-sizing: border-box;
padding: 30px;
}
flex: 1 1 0; は、最後の 0(flex-basis)が肝心です。通常は中身の文字量によって幅が変わってしまいますが、ここを 0 にすることで「すべて同じスタートラインから均等に広がる」というルールが適用されます。
あわせて設定した min-width: 0; も重要です。これがないと、中身に長い単語があった際に「これ以上細くなれない」とそのカードだけが膨らみ、サイズが崩れてしまいます。これを防ぎ、強制的に均等幅をキープさせるための必須設定です。
3. sibling-index() による動的デザイン
ここが今回ご紹介する関数が関係する部分になります。
@supports を使って、最新機能が使えるブラウザにだけ対応させます。
@supports (animation-delay: calc(sibling-index() * 1s)) {
.card {
/* 色の自動計算 */
border-bottom-color: hsl(210, 100%, calc(40% + sibling-index() * 6%));
/* 登場アニメーションの制御 */
animation: slideUp 0.8s ease backwards;
animation-delay: calc(sibling-index() * 0.1s);
}
}
sibling-index() は「1枚目は1、2枚目は2」という数値を返すため、これに 6% を掛け合わせるだけで「後ろのカードほど少しずつ明るくなる」という階調を自動化できます。要素が増減しても、常に美しいグラデーションが維持されるのがメリットです。
この数値はアニメーションの待ち時間(delay)にも活用しています。実行タイミングを 0.1秒ずつズラすことで、カードが流れるように現れる高級感のあるスタッガー演出が手軽に実現可能です。
ここで重要なのが backwards の指定です。これによって、開始前の「透明で下にズレている状態」を待機中もキープできます。これがないと、読み込み時に一瞬カードが表示されてから消えて動き出すという不自然なチラつきの原因になるため、UIの完成度を高めるために欠かせない設定です。
4. ホバー:操作感を守るための優先順位
アニメーションとユーザーの操作がぶつからないように、少し工夫をしています。
.card:hover {
transform: translateY(-15px) !important; /* どんな状態でもホバーを最優先 */
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}
アニメーションが終了した直後は、ブラウザが要素を「元の位置(transform: translateY(0))」に固定しようとする力が強く働きます。ここに !important を添えることで、「ホバー時は上に動かす設定を最優先する」とブラウザに明示し、アニメーション完了後でもホバーアクションが確実に反応するように制御しています。
5. レスポンシブ
スマホやタブレットでも「サイズがバラバラ」にならないための設定です。
@media (max-width: 768px) {
.card {
/* 全体の幅から隙間を引き、2枚で割ることで、画面いっぱいに広げる */
flex: 0 0 calc((100% - 20px) / 2);
}
}
flex: 0 0 …; と指定しているのは、1つ目の数値である flex-grow を 0 に固定するためです。これにより、中途半端な数(奇数など)でカードが余った際も、最後のカードが隙間を埋めようとして巨大化するのを防ぎ、他のカードと同じサイズを美しく維持できます。
また、calc() 関数で 100% の幅を基準に計算することで、どんな画面サイズのスマートフォンで見ても左右に無駄な余白が出ません。デバイスを選ばず、幅いっぱいのレイアウトを保つことができます。
sibling-index() と sibling-count() の合わせ技
ここまでは要素の番号のみを扱ってきましたが、sibling-count() で要素の総数も計算に含めることで、さらに精度の高いデザインが可能になります。
例えば、以下のコードでは「現在の番号 / 全体の数」という比率を計算し、どんな枚数でも完璧なグラデーションになるよう最適化しています。
先ほどのサンプルから変更する部分は、以下の箇所になります。
CSS
/* sibling-index() と sibling-count() の合わせ技 */
@supports (animation-delay: calc(sibling-index() * 1s)) {
.card {
border-bottom-color: hsl(
210,
100%,
calc(30% + (sibling-index() / sibling-count()) * 40%)
);
animation: slideUp 0.8s ease backwards;
animation-delay: calc(sibling-index() * 0.15s);
}
}
色の自動計算(sibling-count() の活用)
sibling-index() / sibling-count() という式を用いることで、「全体に対して自分はどの位置にいるか(0〜1の割合)」を算出しています。たとえば5枚構成の場合、1枚目なら $1 / 5 = 0.2$、5枚目なら $5 / 5 = 1.0$ といった具合に、自分の立ち位置を相対的な数値として取得できます。
この割合計算を取り入れる最大のメリットは、スケーラビリティの向上です。以前のコード(* 6%)のような固定値の加算では、カードが10枚、20枚と増えた際に色が明るくなりすぎ、最終的に真っ白になってしまうリスクがありました。しかし、この数式を使えば、たとえカードが何枚に増えても、指定した範囲内(今回の例では30%から70%の間)で完璧なグラデーションが自動的に生成されます。
sibling関数の本質
これらの関数は、CSSが「自分の立ち位置」と「周囲の状況」を自ら判断することを可能にする、画期的な機能です。
- sibling-index()
- その要素が親要素の中で「何番目の子要素か」を整数で返す関数。
これまでのCSSでは実現できなかった、要素ごとの「個別性」を自動化します。
主に、1番目から順に色を変える、あるいはアニメーションに時間差(ディレイ)をつけるといった、連続性のある演出に欠かせません。 - sibling-count()
- 同じ親を持つ兄弟要素が「全部で何個存在するか」を整数で返す関数。
グループ全体の「ボリューム」を把握するために使用します。
要素の総数に基づいて、1行あたりの幅を正確に計算したり、全体の色の階調を調整したりと、動的なレイアウトの最適化に威力を発揮します。
以前ならJavaScriptを使って計算していたような演出も、これからはCSSだけで完結できます。HTMLの構成が変わってもCSSが自動で調整してくれるため、修正ミスが減り、どんな場面でも使い回しやすい汎用的なコードになります。
以下、今回のサンプルの実装の様子です。
(動画:2分36秒)
最後に
sibling-index() と sibling-count() を使った設計は、単にコードを短くするだけでなく、HTMLの構造に依存しない「自律的なデザイン」を可能にします。
カードが1枚増えても減っても、CSSがその変化を自動で検知し、最適な色、タイミング、サイズへと瞬時に再計算してくれる。これは、「CSSが自ら考えて動く」という、これからのWeb開発における新しい基準になるはずです。
最新ブラウザでの対応が進む中、この技術をいち早く取り入れることで、メンテナンス性に優れたコードを構築できます。ぜひ、皆さんのデザインにも取り入れてみてください。