スナックヨコヤマ

お知らせ

短く書けて上書きもしやすい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になります

Sponsors this Site