@scopeでCSSが整理しやすくなる。コンポーネント周りのセレクタ設計を軽くする実践テクニック

はじめに

コンポーネント単位でCSSを書くようになっても、実際には「この見出しだけ」「このカードの中だけ」をどう安全に狙うかで毎回少し悩みます。クラスを増やして解決できることも多いですが、BEMが重く感じる場面や、既存マークアップに手を入れにくい場面もあります。

そこで試したいのが@scopeです。セレクタの到達範囲をDOMの一部に限定できるので、過剰に長いセレクタを書かなくても、狙った範囲だけを素直に装飾できます。Chrome for Developersの解説でも、「詳細に狙いたいが、詳細すぎるセレクタにはしたくない」という悩みに対する答えとして紹介されています。

この記事では、@scopeの基本から、既存コンポーネントの整理にどう効くか、そして実務でありがちな「一部だけ除外したい」ケースまでまとめます。

機能やライブラリの概要

@scopeは、特定のDOMサブツリーの中だけでセレクタを有効にするCSSのat-ruleです。MDNでは「過度にspecificityの高いセレクタを書かずに、特定のDOM部分木だけを正確に狙える」と整理されています。

たとえば.card .body .title aのような長いセレクタを書かなくても、@scope (.card)の中でaと書けば、そのカードの外までは届きません。しかも、@scopeの前置きに書いたルートセレクタは、内側のセレクタのspecificityに加算されません。ここがかなり実務向きです。

覚えておきたいポイントは次の3つです。

  • @scope (.card) { ... }のように、適用範囲の上限を決められる
  • to (...)を付けると、途中までを対象にする「ドーナツ状」の絞り込みができる
  • :scopeでスコープルート自身も装飾できる

「スタイルの完全隔離」ではなく「セレクタの到達範囲の制御」と考えると理解しやすいです。

基本の使い方

まずは、お知らせカードの中だけリンク色を変える例です。このコードでは、.notice-cardの内側だけにスタイルを閉じ込めています。

HTML
<section class="notice-card">
  <h2>アップデート情報</h2>
  <p>
    新しい管理画面については
    <a href="/docs/admin-redesign">こちらのガイド</a>
    を確認してください。
  </p>
</section>

<section class="profile-card">
  <h2>プロフィール</h2>
  <p>
    制作者については
    <a href="/about">プロフィールページ</a>
    にまとめています。
  </p>
</section>
CSS
@scope (.notice-card) {
  :scope {
    padding: 24px;
    border: 1px solid #cbd5e1;
    border-radius: 20px;
    background: #f8fafc;
  }

  h2 {
    margin-block: 0 0.75rem;
    font-size: 1.2rem;
  }

  a {
    color: #0f766e;
    text-decoration-thickness: 0.08em;
  }
}

このコードでやっているのは、.notice-cardをスコープルートにして、その中だけh2aをスタイリングしていることです。aという短いセレクタでも、カードの外までは届きません。

クラス名を細かく増やさずに済むので、既存HTMLをなるべくそのまま活かしたい案件と相性がいいです。

便利な使いどころ

@scopeが特に便利なのは、「マークアップはそこそこ安定しているが、クラス設計をこれ以上増やしたくない」場面です。

  • CMSのリッチテキスト出力を、ブロック単位で整えたいとき
  • 既存サイトの一部コンポーネントだけデザインを差し替えたいとき
  • 同じ要素名が多いカード群やティザー群を整理したいとき
  • 外部埋め込みやWYSIWYG出力を局所的に整えたいとき

たとえば記事カードの一覧では、h2imgaが何度も出てきます。そこで毎回.article-card__title-linkのような専用クラスを振るより、まずは.article-cardをルートにして必要な範囲だけ整えるほうが速いケースがあります。

また、Chrome for Developersの解説でも触れられているように、@scopeはspecificityの調整がしやすいのが強みです。ルート側のセレクタが重くても、内側のimga自体のspecificityは必要以上に上がりません。あとからテーマ上書きを入れるときも助かります。

応用コード

次は、「カード全体は整えたいが、本文内の図版だけは除外したい」というケースです。to (...)を使うと、途中の要素を境界にして対象範囲を切れます。

HTML
<article class="feature-card">
  <h2>製品紹介</h2>
  <img src="/images/hero.jpg" alt="製品のヒーローイメージ" />

  <div class="feature-card__body">
    <p>導入イメージを短く紹介します。</p>

    <figure>
      <img src="/images/chart.png" alt="比較表の図版" />
      <figcaption>比較表</figcaption>
    </figure>
  </div>
</article>
CSS
@scope (.feature-card) {
  :scope {
    display: grid;
    gap: 16px;
    padding: 24px;
    border-radius: 24px;
    background: #fff;
    box-shadow: 0 18px 40px rgba(15, 23, 42, 0.08);
  }

  h2 {
    margin: 0;
    font-size: clamp(1.25rem, 2vw, 1.6rem);
  }
}

@scope (.feature-card) to (figure) {
  img {
    border-radius: 18px;
    display: block;
    inline-size: 100%;
    object-fit: cover;
  }
}

このコードでは、.feature-card配下のimgにだけ丸みを付けつつ、figureの中に入った図版は除外しています。ヒーロー画像だけを整えたいのに、本文中の図版まで同じ見た目になってしまう、というのは地味によくある事故です。

こういうとき、従来なら.feature-card > img.feature-card__media imgのようにDOM構造へ強く依存しがちでした。@scopeto (...)を使うと、「この範囲までは対象、ここから先は除外」という考え方で整理しやすくなります。

注意点

  • @scopeはスタイル隔離ではない
    Chrome for Developersの解説でも強調されていますが、@scopeはセレクタの到達範囲を制御する機能です。継承プロパティはスコープの下限を越えて伝わることがあります。
  • :scope&は同じではない
    :scopeはマッチしたスコープルート自身、&はルート指定セレクタの展開に近い動きをします。specificityの扱いも変わるので、読みやすさ優先で使い分けたほうが安全です。
  • 古い環境では代替セレクタを残す判断も必要
    MDNの互換情報は必ず確認したいです。2026年3月31日時点では、関連するCSSScopeRuleはBaseline 2025として案内されていますが、案件によっては対象ブラウザ次第で段階導入が現実的です。
  • クラス設計を完全に置き換えるものではない
    再利用性の高いデザインシステムでは、クラス命名の明快さが必要な場面もあります。@scopeは「クラスを減らす魔法」ではなく、セレクタ設計を軽くする選択肢として使うのがちょうどいいです。

まとめ

@scopeは、CSSを激変させる派手な機能ではありませんが、既存コンポーネントの整理や局所的な上書きにかなり効きます。特に「長いセレクタは避けたいが、素の要素セレクタでは広すぎる」という中間地帯で使いやすいです。

いきなり全面採用するより、カードUIやリッチテキストの装飾、CMS出力の整形などから試すのがおすすめです。セレクタの責務が少し軽くなるだけで、あとから読むときの気楽さがかなり変わります。

ポイント

  • @scopeは、セレクタを長くしすぎずにコンポーネント内だけを狙いたいときに効く
  • to (...)を使うと、図版や本文など一部だけ除外する設計がしやすい
  • スタイル隔離ではなくセレクタ制御なので、継承や対応ブラウザは別途意識しておく

参考リンク

read next