【実務・中級編】 擬似クラス :first-child と :last-child – CSS実践ガイド

おい、調子はどうだ?
今日も元気にデザインカンプと睨めっこしながら、意図通りに動かないCSSに頭を悩ませているところか?痛いほど気持ちは分かる。フロントエンドの道を進んでいれば、誰もが一度は「なぜこの要素だけ余白が狂うんだ……」という泥沼にハマるものだ。

さて、今回は中級への階段を着実に登っているお前らに、CSSの基本でありながら、現場のコードの美しさと保守性を劇的に変える`:first-child`と`:last-child`について、俺の知見を包み隠さず叩き込んでやろうと思う。

公式ドキュメントを見れば「兄弟要素の最初と最後の要素を選択する」なんて、眠たくなるような一行が書いてある。だがな、実務の現場では、この2つの擬似クラスをどう使いこなすかが、お前のマークアップの「格」を決めるんだ。

心して聞いてくれ。

—

なぜ `:first-child` / `:last-child` が実務でこれほど重宝されるのか?

フロントエンドの現場で最も多いUIの要件の一つに、「リストやカードが縦(または横)に並ぶとき、要素と要素の間にだけボーダーやマージンを入れたい」というものがある。

駆け出しの頃の俺やお前なら、こう書いていただろう。
「すべての要素に `margin-bottom: 20px;` を入れて、最後の要素だけ `.last` クラスをつけて `margin-bottom: 0;` にする」と。

ちょっと待て。それ、本当にイケてるか?
CMSで動的に出力されるリストや、APIから非同期でゴリゴリ流し込まれるカードの群れに対して、わざわざJavaScriptで「最後の要素にだけクラスを付与する」なんて無駄なロジックを組むのか? あるいは、バックエンドのエンジニアに「すいません、最後の要素判定のテンプレートタグを追加してください……」と頭を下げるのか?

違うよな。そんな無駄な工数は、俺たちの技術で完全にハックして消し去るべきだ。
そこで登場するのが `:first-child` と `:last-child` だ。これらを使えば、HTML側を一切汚さず、CSSの構造セレクタだけで「最初と最後」を完璧に制御できる。これがプロの仕事というもんよ。

—

ブラウザの裏側の話:こいつらはどうやって要素を見つけているのか?

ちょっと小難しい話をしようか。ブラウザがCSSを適用するとき、裏側ではDOMツリーを走査している。
`:first-child` や `:last-child` に出会ったとき、ブラウザは親要素(Parent Element)を基準にして、その子どもたちを上から(あるいは下から)順にスキャンする。

そして、「お前、親から見て本当に最初の子(最初のエレメント)か?」を確認する。
ここで一つ、中級のエンジニアがよくやらかす最大の罠を教えておこう。

⚠️ 現場で一番多いバグ:「あれ、効かない?」の正体

例えば、こんなHTMLがあったとする。

おすすめの記事

記事1
記事2

ここで、「最初の `.card` にだけ上パディングを消したい!」と思って、こう書いたとする。

.card:first-child {
padding-top: 0;
}

おい、これでお前、期待通りに動くと思うか?
答えは「絶対に動かない」だ。

なぜだか分かるか? ブラウザの視点に立ってみろ。
`.card:first-child` が探しているのは、「親(`.card-container`)にとっての最初の子ども」だ。
このHTMLの場合、`.card-container` の最初の子どもは `

` タグだ。`.card` ではない。 つまり、ブラウザはこう判断する。 「おい、この ` ` は `.card` クラスを持ってないからスルーだな。お、次の要素……こいつは `.card` だが、最初の子じゃないから対象外だ」 結果、どの `.card` にもスタイルは適用されない。 この挙動にハマって「CSSのバグだ!」と叫ぶ若手を何人も見てきた。バグじゃない、仕様の理解不足だ。 親要素の中にある「特定のタグやクラスの最初」を狙いたいなら、ここでは解説しないが `:first-of-type` を使うか、あるいはHTMLの構造をちゃんとラッパー(` `など)で囲って整理する必要がある。`:first-child` はあくまで「純粋な兄弟の中の最初っ子」なのだから。 — 実務で即コピペして使える! 鉄板パターン集

理屈はここまでにして、明日から即座に現場で使える実践的なコードを見せてやろう。

パターン1:リストの境界線(ボーダー)の制御

リストアイテムの間に線を入れるとき、最初と最後に線が出てしまうと一気にダサくなる。これを `:first-child` と `:last-child` でスマートに消す。

.item-list {
list-style: none;
padding: 0;
margin: 0;
border-top: 1px solid #e0e0e0; / リスト全体の一番上の境界線 /
}

.item-list__item {
padding: 16px 20px;
border-bottom: 1px solid #e0e0e0; / 各アイテムの下境界線 /
}

/

  • チップス:
  • 全体に上下ボーダーを引いた上で、最初と最後の不要なボーダーを相殺する。
  • これにより、CSSの記述が非常にシンプルになる。

/
.item-list__item:first-child {
/ 必要に応じて調整 /
}

.item-list__item:last-child {
/ 最後の要素の下ボーダーは親のborder-topと被るので消す、などの調整が可能 /
/ 実は現代のCSSでは :not(:last-child) や gap を使うアプローチもあるが、古いブラウザや特定のレイアウトではこれが確実 /
}

よりモダンなアプローチとして、マージンやパディングの相殺を防ぐために `:not(:last-child)` を使うのも実務ではよくあるテクニックだ。

/ 最後の要素以外にだけ、下マージンをゴリっと付与する /
.card-item:not(:last-child) {
margin-bottom: 24px;
}

これ、めちゃくちゃ便利だろ?「最後の要素だけマージンを `0` にリセットする」という無駄な記述を一生書かなくてよくなる神の一行だ。感謝してくれていい。

—

シニアからのプロの心得

CSSのセレクタは、ただ「要素を指定するだけの道具」ではない。
HTMLの構造(セマンティクス)とCSSのセレクタの相性をどう設計するか、その「組み合わせの美学」なのだ。

`:first-child` や `:last-child` を適切に使いこなせるようになると、無駄なクラス名(`.first`, `.last`, `.mb-0` など)をHTMLにベタ書きする悪癖から脱却できる。HTMLはあくまで文書構造の記述に専念させ、見た目の制御はCSSの構造擬似クラスに任せる。この関心の分離ができてこそ、変更に強く、誰が触っても壊れない堅牢なフロントエンドアーキテクチャが構築できる。

さあ、理屈は分かったな。
お前のエディタを開いて、今プロジェクトで書いてるその泥臭いマージン調整のコードを、この知見を使って美しくリファクタリングしてみろ。

何か詰まったら、いつでも俺に聞きにこい。応援しているぞ。

コメント

タイトルとURLをコピーしました