【実務・中級編】 is関数型疑似クラス – CSS実践ガイド

おい、調子はどうだい?
最近、後輩のコードレビューをしていて「おっ、やるじゃん」と感心させられることが増えたんだが、同時に「あー、まだ古いCSSの書き方に囚われてるな」ってもどかしく思う瞬間も多いんだよね。

特に、コンポーネントが複雑化してくると、セレクタがどんどん長くなって、詳細度(Specificity)のモンスターが産声を上げ始める。気づけば `!important` の泥沼に引きずり込まれ、デバッグで深夜残業……なんて悪夢、君も経験したことがあるんじゃないか?

今回は、そんなCSS地獄から私たちを救い出してくれる救世主、`:is()` 疑似クラスについて徹底的に解説しよう。
教科書通りの退屈な説明は抜きにして、現場の戦場でどう立ち回るための武器になるのか、俺のノウハウをすべて叩き込んでやるよ。心してついてきな。

—

1. なぜ `:is()` なのか? 現場のエンジニアを悩ませる「詳細度地獄」の正体

まずは、普段君が直面しているであろう現実から目を背けずに話そう。
例えば、モーダルの中にあるボタン、あるいはカードコンポーネントの中にあるリンクなど、「特定の親要素配下にある、多様なインタラクティブ要素」を一括してスタイリングしたいとき、君ならどう書く?

従来のCSSだと、こうなるよな。

/ 従来の書き方:セレクタが肥大化し、詳細度が跳ね上がる /
.modal-wrapper button:hover,
.modal-wrapper a:hover,
.modal-wrapper input[type=”submit”]:hover {
background-color: var(–color-primary-hover);
}

パッと見、何をやっているかは分かる。だが、この書き方には致命的な問題が2つある。

1. DRY原則(Don’t Repeat Yourself)に反している: `.modal-wrapper` や `:hover` を何度もタイピングさせられている。指が疲れるし、タイポの温床だ。
2. 詳細度が強すぎる: クラス1個(`0-1-0`)と要素型セレクタ、さらに疑似クラスが混ざることで、このセレクタ全体の詳細度が計算されてしまう。後から「特定の条件でこのスタイルを上書きしたい!」と思ったときに、詳細度の壁に阻まれて発狂することになる。

ここで登場するのが、今日の本丸である `:is()` 関数型疑似クラス だ。

—

2. `:is()` の基本仕様と「詳細度の魔法」

`:is()` は、複数のセレクタをカンマ区切りで受け取り、その「いずれか」に一致する要素を選択する強力な関数型疑似クラスだ。

こいつの最大の武器であり、フロントエンドエンジニアが涙を流して喜ぶポイント。それは……
「`:is()` の中にあるセレクタの群れの中で、最も詳細度が高いものの影響を受けるが、全体としての `:is()` 自体の詳細度は、それに含まれる最も強い詳細度ではなく、コンテキストに依存する(基本的には最も強いセレクタ単体の詳細度になり、かつ不要に釣り上げない)」 という仕様だ。

……すまない、言葉だと少し硬いな。要するにこういうことだ:

> 「`:is()` の中のセレクタで一番強いやつの詳細度だけが採用され、余計な加算がされない。しかも、マッチしなかった無駄なセレクタのせいで詳細度が上がったりしない。」

百聞は一見にしかず。先ほどのコードを `:is()` で書き直してみよう。

/ :is() を使ったモダンな書き方 /
.modal-wrapper :is(button, a, input[type=”submit”]):hover {
background-color: var(–color-primary-hover);
}

見てくれ、この圧倒的な美しさとスマートさを!
親要素の指定は1回で済み、対象となる要素を `:is()` の中にスッキリとリスト化できる。DRY原則の美しい勝利だ。

—

3. ブラウザの裏側で何が起きているか?(パフォーマンスの真実)

シニアを名乗るなら、ただ「便利だから使え」ではダメだ。ブラウザが裏側でどう動いているかも理解しておこう。

ブラウザのレンダリングエンジン(BlinkやGeckoなど)は、CSSをパースしてスタイルを適用する際、セレクタを右から左(Key Selectorから親方向)に向かってマッチングしていく。

従来の長いカンマつなぎのセレクタ:
`û.modal-wrapper button:hover`, `.modal-wrapper a:hover` … は、ブラウザから見ると「別々の独立した完全なセレクタ」としてそれぞれ評価されていた。

一方、`:is()` を使った場合、ブラウザはこれを「内部的なグループ化」として最適化して処理する。
モダンブラウザのパーサーは非常に優秀で、`:is()` の中身を効率的にツリー構造にマッピングするため、メモリ効率やスタイル計算のパフォーマンスにおいても、従来の冗長なセレクタと比べて遜色ない、あるいはそれ以上に最適化されやすい環境が整っている。

ただし、ネスト(入れ子)のしすぎには注意しろよ。 `:is()` の中にさらに `:is()` をブチ込むような狂気的なコードを書くと、さすがのブラウザも解釈に苦しみ、コードの可読性も地の底に落ちる。何事も「適度」が肝心だ。

—

4. 【実務で即コピペ】コンポーネント開発を爆速にする実践的コード例

理論はこの辺にして、明日から君のプロジェクトで即座に使える、実務直結のパターンをいくつか授けよう。

パターンA:フォーム要素の状態管理を一網打尽にする

ユーザー入力を受け付けるフォーム周り、特に `input`, `select`, `textarea` あたりのスタイリングで `:is()` は真価を発揮する。

/ フォームの共通インタラクティブ状態(フォーカス時・ホバー時)を一括制御 /
.form-control :is(input, select, textarea) {
width: 100%;
padding: 0.75rem 1rem;
border: 1px solid var(–color-border);
border-radius: var(–radius-md);
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

/ ホバー時の挙動(ただし無効化されている要素は除く) /
.form-control :is(input, select, textarea):hover:not(:disabled) {
border-color: var(–color-border-hover);
}

/ フォーカス時の挙動 /
.form-control :is(input, select, textarea):focus-visible {
outline: none;
border-color: var(–color-primary);
box-shadow: 0 0 0 3px var(–color-primary-alpha);
}

:not(:disabled) との組み合わせを見てくれ。これができるから `:is()` は手放せない。無駄なCSSの上書き定義を書く必要が一切なくなる。

パターンB:見出し要素のレスポンシブ・タイポグラフィ

カードやセクションのタイトルなど、構造によってタグが変わる(`h2`, `h3`, `h4` など)が、見た目のスタイルは共通化したい場合にも `:is()` は最高にクールに決まる。

/ どの見出しタグであっても、カード内であれば同じスタイルを強制する /
.card :is(h2, h3, h4) {
font-size: var(–font-size-lg);
font-weight: 700;
line-height: 1.4;
color: var(–color-text-main);
margin-bottom: var(–space-sm);
}

/ リンクが含まれている場合の例外処理もスマートに /
.card :is(h2, h3, h4) a {
color: inherit;
text-decoration: none;
}

.card :is(h2, h3, h4) a:hover {
text-decoration: underline;
color: var(–color-primary);
}

どうだ? メンテナンス性が劇的に向上するのがイメージできるはずだ。将来的に `h5` も追加しようとなったときも、`:is(h2, h3, h4, h5)` とカンマで足すだけで終わる。

—

5. 先輩からの最後の忠告(注意点とブラウザサポート)

最後に、実務で導入するにあたっての注意点をいくつか。

1. フォールバックはもう必要ない?
かつては `:is()` の前身である `-webkit-any()` や `-moz-any()` が乱立していた暗黒時代があったが、現在のモダンブラウザ(Chrome, Safari, Firefox, Edgeの主要バージョン)における `:is()` のサポート率は 98%を超えている。B2Bの特殊なレガシーブラウザ(IE11など)をサポート外とするプロジェクトであれば、今日からノータイムで導入していい。
2. マッチしなかったときの挙動
`:is()` の中に含まれるセレクタが1つでも構文エラー(タイポなど)を起こした場合、その `:is()` 全体が無効化される という厳格な仕様がある。従来のカンマつなぎであれば「エラーのあった行だけ無視されて他は効く」という挙動だったが、`:is()` は「オール・オア・ナッシング」だ。タイポにはいつも以上に気をつけよう。

—

まとめ

CSSの進化は早い。数年前の「常識」は、今日の「アンチパターン」になり得る。
`:is()` 疑似クラスを使いこなすことは、単にコード量を減らすだけじゃない。「詳細度の呪縛」から解放され、保守性が高く、チームメンバー誰もが読みやすいクリーンなデザインシステムを構築するための強力なパスポートだ。

さあ、理屈はわかったな?
今すぐエディタを開いて、君が昨日書いたあの冗長なセレクタの山を、`:is()` で美しくリファクタリングしてやれよ。きっとコードレビューの時に、チームの誰もがその美しさに驚くはずだ。

困ったときはいつでも俺のところに来い。一緒に最高のコードベースを作り上げていこうぜ!

コメント

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