こんにちは!フロントエンドの現場を渡り歩いているチーフアーキテクトの私です。
CSSを書き始めたばかりの頃って、次から次へと新しい書き方が登場して「もうついていけないよ…」ってめまいがしそうになりますよね。特に、ここ数年でCSS界の主役に躍り出た最強の機能、`:has()`擬似クラスなんて名前を聞いただけで「なんだか難しそう……」とそっとタブを閉じたくなる気持ち、すごくよく分かります。
でも、安心してください。今日お話しする内容を読み終える頃には、「なんだ、そういうことか!CSSって案外優しいじゃん」ってきっと思ってもらえるはずです。
今日は、この `:has()` がどれだけ私たちのコーディングライフを救ってくれるのか、身近な例えを交えながら、ゆっくり、丁寧に紐解いていきましょう。
—
そもそも `:has()` ってどんなもの?
これまでのCSSは、いわば「上から下へ、親から子へ」流れる一方通行の交通整理みたいなものでした。親のスタイルを変えるには、親自身にクラスをつける必要があったんです。
「なかに特定の要素が入っているときだけ、親の見た目を変えたい」
そんな願いを叶えるために生まれたのが `:has()` です。
イメージしやすいように、身近なものに例えてみましょう。
スーパーのレジカゴを思い浮かべてください。普段はただのカゴ(親)ですが、中に「お酒(特定の要素)」が入った瞬間、店員さんが「年齢確認シール(特別なスタイル)」を貼りますよね。
CSSの `:has()` は、まさにこのレジカゴの役割をしてくれます。
「中に〇〇が入っていたら、私(親)の見た目を変えるよ!」 という、とっても賢い番人なんです。
—
基本のキ:画像があるカードだけデザインを変える
まずは、よくあるカードUIを例に見てみましょう。
ブログの一覧などで、「サムネイル画像があるカード」と「文字だけのカード」が混ざっていることってありませんか?
「画像があるカードだけ、レイアウトを左右に分けたいな……でも、カード自体のHTML構造は全部同じにしたい!」
そんなとき、昔ならJavaScriptを使うか、HTML側にせっせと `has-image` みたいなクラスを手作業でつけていました。
でも、もうその必要はありません。`:has()` を使えば、CSSだけで一発解決です。
記事のタイトルが入ります
ここに記事の簡単な説明文が入りますよ。
お知らせのタイトルです
画像がないパターンです。
このとき、CSSは次のように書きます。
/ カードの中に img 要素が含まれているときだけ発動! /
.card:has(img) {
display: grid;
grid-template-columns: 150px 1fr; / 画像とテキストを横並びにする /
gap: 16px;
}
たったこれだけです!HTMLを汚さずに、「画像を持っている子」を見つけて親側がシュッと変身してくれる。これが `:has()` の優しさであり、強力なところです。
—
子孫・隣接結合子との組み合わせで、さらに世界が広がる!
さて、ここからが少しステップアップした本番です。
`:has()` の格好いいところは、カッコの中に「子孫(中にいる子)」や「隣接(すぐ隣にいる兄弟)」といった複雑な関係性を組み込める点にあります。
「うわ、なんか専門用語が出てきた……」と思いました?大丈夫、すぐにかみ砕いて説明しますね。
パターン1:子孫結合子(スペース)を使った条件指定
「カードの中に、さらに『セール中』というバッジ(タグ)が含まれているとき」を考えてみましょう。
/ .card の中に、さらに .badge–sale が含まれているか? /
.card:has(.badge–sale) {
border: 2px solid #ff4757; / 目立つ赤い枠線をつける /
background-color: #fff5f5;
}
スペース(子孫結合子)を使うことで、「直下じゃなくても、とにかく内部のどこかにその要素がいればOK!」という太っ腹な条件を作ることができます。
パターン2:隣接結合子(`+`)を使った「すぐ後ろ」の条件指定
ここからが実務で泣くほどありがたいテクニックです。
例えば、フォームの入力欄(``)があって、そのすぐ隣にエラーメッセージ(`.error-message`)が表示されるデザインを想像してください。
「エラーメッセージが存在するときだけ、入力欄の枠線を赤くしたい」
昔なら、JavaScriptでエラーの有無を監視してクラスをつけ外ししていましたよね。でも、`:has()` ならCSSだけで完結します。
/ 「inputのすぐ後ろに .error-message がいる」状態のとき、inputを赤くする /
.form-group:has(input + .error-message) input {
border-color: #e74c3c;
background-color: #fdf2f2;
}
すごいでしょう? `input` のすぐ後ろ(`+`)に `.error-message` が「お邪魔している」という関係性を、親である `.form-group` がキャッチして、中の `input` をスタイリングしているんです。
これぞ、フロントエンドの職人芸とも言える美しい連携プレイです。
—
実務でつまずきやすいポイントと、温かいアドバイス
ここまで読んで、「よし、明日から全部 `:has()` に置き換えちゃおう!」と思ったそこのあなた。ちょっとだけ、先輩から現場のリアルなアドバイスをさせてください。
1. ブラウザの対応状況はもう大丈夫?
「新しい機能ってことは、古いブラウザじゃ動かないんじゃないの?」という心配、すごくよく分かります。
結論から言うと、現在のモダンブラウザ(Safari、Chrome、Edge、Firefoxのすべて)において、`:has()` はすでに完全にサポートされています。「安心して使っていい時期」はとっくに過ぎていますので、胸を張ってコードに書いてくださいね。
2. 「何でもかんでも `:has()`」はちょっと待って
便利だからといって、複雑すぎる条件を何重にも `:has()` で包み込むと、いざ後からデザインを直そうとしたときに「あれ、これどこが効いてるんだっけ?」と迷子になってしまいます(CSSの迷子、本当につらいですよね)。
コードを書くときは、
「あ、ここはHTMLのクラスをシンプルに付け替えた方が、他の人にとって読みやすいかも」
「ここはJavaScriptの介入を減らすために `:has()` が大正解だな」
という塩梅(あんばい)を、少しずつ掴んでいってください。
—
おわりに
CSSの `:has()` は、私たちが長年頭を悩ませてきた「親要素のスタイリング問題」を鮮やかに解決してくれる、魔法のような道具です。
最初は少し難しく感じるかもしれませんが、今日お話しした「レジカゴの例え」や「隣にいるお友達の例え」を思い出してもらえれば、きっと自然と手が動くはずです。
もしコードがうまくいかなくても、焦る必要は全くありません。ブラウザの開発者ツールを開いて、一つひとつ要素を眺めながら、「あ、ここに隠れてたんだね」と対話するように試行錯誤してみてください。その泥臭い試行錯誤の積み重ねこそが、あなたを確かなエンジニアにしてくれます。
あなたのWeb制作ライフが、今日から少しでも楽しく、軽やかになりますように。
それではまた、次の現場でお会いしましょう!

コメント