`:nth-child(an+b)` の深淵:なぜ我々は「魔法の数字」に苦しむのか
フロントエンドの現場で、「3つおきにデザインを変えたい」「偶数番目だけ余白を消したい」といった要求に出くわしたとき、君はどうする? 適当にクラスを振りまくか、JavaScriptでDOMを操作するか。
もし君が「CSSだけでエレガントに解決したい」と願うなら、避けては通れないのが `:nth-child(an+b)` という名の、一見すると数学の試験のような構文だ。多くのエンジニアが「なんとなく」で使い、意図しない挙動に頭を抱えるこの構文。今日はその「中身」を、ブラウザの視点から紐解いていこう。
—
`:nth-child(an+b)` の構造を解剖する
この記法、実はシンプルだ。`a` は「周期」、`n` は「0からのカウンター」、`b` は「オフセット(開始位置)」を意味する。
- `a`: 周期的な間隔。ここが3なら3つおき、4なら4つおきだ。
- `n`: 0, 1, 2, 3… と自動的に増えていくブラウザ側の魔法の変数。
- `b`: どこからカウントを開始するか。
ブラウザは内部的に、DOMツリーを走査しながらこの式を計算し、マッチした要素にフラグを立てる。`n=0` からスタートして、結果が正の整数、かつ範囲内であればそれが「選ばれた要素」となるわけだ。
実務でよく使う「定型句」の正体
- `:nth-child(3n)` : 3, 6, 9番目…(周期性)
- `:nth-child(3n+1)` : 1, 4, 7番目…(+1することで最初の要素から開始)
- `:nth-child(n+4)` : 4番目以降すべて(特定の要素以降を制御)
—
現場で「泣かない」ための実践コード
よくあるのが、グリッドレイアウトで「右端の余白を消す」というケースだ。これをJSでやるのはナンセンスだ。CSSでスマートに処理しよう。
/ 3カラムのグリッドで、右端(3の倍数)の余白を消す例 /
.grid-container {
display: flex;
gap: 20px;
}
.item {
width: calc((100% – 40px) / 3);
}
/ 3番目、6番目、9番目…の右側だけmarginを0にする /
.item:nth-child(3n) {
margin-right: 0;
}
ここで重要なのは、「もし要素が動的に増減しても、CSSが自動的に再計算してくれる」という点だ。これがクラス付与との決定的な違いであり、保守性の高さに直結する。
—
さらに一歩先へ:`not` や `has` との合わせ技
最近のモダンブラウザでは、これ単体で戦う必要はない。`nth-child` は「特定の条件下でのみ除外する」といった条件分岐にも非常に強い。
/ 「最初の3つ」と「最後の3つ」以外にスタイルを当てるテクニック /
/ n+4 で4番目以降を選択し、かつ :not(:nth-last-child(-n+3)) で後ろから3つ目までを否定する /
.list-item:nth-child(n+4):not(:nth-last-child(-n+3)) {
border-bottom: 1px solid #ccc;
padding: 10px 0;
}
このコード、最初は難解に見えるかもしれない。だが、「4番目以降から、後ろから数えて3つ目までの要素を引く」という論理が分かれば、これほど強力な武器はない。リストの先頭と末尾を除いた「中間部分」だけに線を引くようなUIは、これ一発で終わる。
—
エンジニアとしての心構え:ブラウザを信じるな
最後に、シニアとして君に一つだけアドバイスを送る。
`:nth-child` を使うときは、「もし要素の構造が変わったら、この計算式はどう崩れるか?」を常に想像してほしい。HTMLの構造が複雑になりすぎると、CSSの計算式は途端に脆くなる。
- 構造をフラットに保つ: 可能な限り `:nth-child` が当たる要素を同じ階層に揃える。
- 複雑すぎる式は避ける: `(4n+3)` 以上に複雑な式が必要になったら、それはCSSの責任ではなく、HTML構造自体を見直すべきサインだ。
CSSは「デザインを記述する言語」だが、同時に「DOMとの対話」でもある。この計算式を使いこなせれば、君のコードは驚くほど軽量になり、JSの介入を最小限に抑えた、堅牢で美しいフロントエンドが実現できるはずだ。
次はぜひ、君のプロジェクトの「余白問題」をこの計算式で切り刻んでみてくれ。応援しているよ。

コメント