お知らせ
短く書けて上書きもしやすいCSS
2025.11.06お知らせ

:is() と :where() の使い分け
前回の「:has()で“中身に応じて見た目を出し分ける”」記事(👉https://yoyo9140.site/first-time-has/ )の続きです。
今回は、セレクタを短く書けるうえに、**上書きのしやすさ(特異性)**もコントロールできる :is() と :where() をやさしく解説します。
どちらも「候補をひとまとめ」にできますが、決定的な違いは 特異性(=どれだけ強く効くか)。
:is()… 中の候補のうち 一番強い特異性を引き継ぐ:where()… 常に0(超よわよわ)。ベースや初期化に最適
この記事は 結論 → 基本 → 実例 → 落とし穴 → チートシート の順で、コピペOKのコードと一緒にサクッと整理します。
TL;DR(最初に結論だけ)
:is()… 「負けたくない・ちゃんと効いてほしい」時に
→ 最終ルールや擬似クラス(hover 等)の束ねに向く:where()… 「あとで上から塗り替えたい」時に
→ ベース・初期化・ユーティリティに向く- 迷ったら:土台=
:where()/仕上げ=:is()
1. 基本:何が「短く・読みやすく」なるの?
Before(長い&重複しがち)
.nav li > a:hover,
.nav li > a:focus-visible,
.nav li > a[aria-current="page"] {
color: var(--primary);
}
After(:is() で一気に短縮)
.nav li > a:is(:hover, :focus-visible, [aria-current="page"]) {
color: var(--primary);
}
ベースを“弱く”置くなら :where()
/* ベース(あとから上書きしやすい) */
:where(.prose) :where(h1, h2, h3) { margin-block: .8em; }
ポイント:見た目の範囲は同じ。違うのは特異性だけ。
2. 使い分けのコツ(1分で感覚をつかむ)
:is()… 「ここは効いてほしい」とき
例:hover / focusなど状態の束ね、最後の上書き:where()… 「あとで上書き前提」のとき
例:タイポのベースや初期化(utility系)
強さのイメージ:is() >(ふつうのセレクタ)> :where()(常に弱い)
3. よくある実例(コピペOK)
3-1. 見出しまとめ(どれが来ても同じ余白)
.article :is(h1, h2, h3) { margin: .8em 0; } /* 仕上げ */
ベースで置きたいなら
:where(.article) :where(h1, h2, h3) { margin: .8em 0; } /* 上書きしやすい */
3-2. 擬似状態の束ね(hover / focus / current)
.nav a:is(:hover, :focus-visible, [aria-current="page"]) {
color: var(--primary);
text-decoration: underline;
}
3-3. テキストリンクだけ下線(:has()との合わせ技)
.article a:not(:has(img, svg)):is(:hover, :focus-visible) {
text-decoration: underline;
}
3-4. いろんな入力要素を一括スタイル
.form :is(input, select, textarea) {
font: inherit;
padding: .5rem .75rem;
border: 1px solid var(--border, #e5e7eb);
border-radius: .5rem;
}
3-5. 表のヘッダ&フッタだけスタイル
.table :is(thead, tfoot) th {
background: color-mix(in srgb, currentColor 8%, transparent);
}
4. 落とし穴と回避策(ここだけ覚えればOK)
4-1. 「強すぎて上書きできない…」
- 原因:
:is()は 中の一番強い特異性を持つ - 回避:ベースは
:where()に/セレクタを浅く
/* 強すぎる例(.title.strong を引きずる) */
.card :is(h2, h3, .title.strong) { ... }
/* 弱める(上書きしやすい) */
:where(.card) :where(h2, h3, .title) { ... }
4-2. 範囲広すぎ&ネスト深すぎは重い
- 対策:スコープを絞る(
.articleなど親クラス配下に)
4-3. ブラウザ対応
- 2025年時点:主要ブラウザは実用OK
- どうしても古い環境まで同一動作にしたいなら、素直に列挙も検討
5. 設計の型(このパターンを真似すればハズレない)
型A:ベースは弱く(:where())、仕上げは強く(:is())
/* ベース(弱い) */
:where(.prose) :where(h1, h2, h3) { margin: .8em 0; color: #0f172a; }
/* 仕上げ(強い) */
.prose h1:is(.xl, .xxl) { font-size: clamp(2rem, 4vw, 3rem); }
型B:状態の束ね
.btn:is(:hover, :focus-visible, :active) { transform: translateY(-1px); }
型C:ターゲットまとめ + 条件追加
/* “入力っぽいもの全部” かつ “無効ではない” */
form :is(input, select, textarea):not(:disabled) { opacity: .95; }
6. コピペ用チートシート
/* まとめて:状態 */
.link:is(:hover, :focus-visible, [aria-current="page"]) { text-decoration: underline; }
/* まとめて:要素 */
.prose :is(h2, h3, h4) { margin-block: .8em; }
/* ベースを弱く置く */
:where(.prose) :where(h2, h3, h4) { margin-block: .8em; }
/* :has() と併用(テキストリンクだけ装飾) */
.article a:not(:has(img, svg)):is(:hover, :focus-visible) { text-decoration: underline; }
7. まとめ
:is()… 重複を畳みつつ、強さはそのまま(仕上げに):where()… 重複を畳みつつ、強さゼロ(ベースに)- 基本方針:土台=
:where()/仕上げ=:is() - おまけ:
:has()と合わせると、短く・読みやすく・壊れにくいCSSになります