はじめに
コンポーネント単位で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の内側だけにスタイルを閉じ込めています。
<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>@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をスコープルートにして、その中だけh2やaをスタイリングしていることです。aという短いセレクタでも、カードの外までは届きません。
クラス名を細かく増やさずに済むので、既存HTMLをなるべくそのまま活かしたい案件と相性がいいです。
便利な使いどころ
@scopeが特に便利なのは、「マークアップはそこそこ安定しているが、クラス設計をこれ以上増やしたくない」場面です。
- CMSのリッチテキスト出力を、ブロック単位で整えたいとき
- 既存サイトの一部コンポーネントだけデザインを差し替えたいとき
- 同じ要素名が多いカード群やティザー群を整理したいとき
- 外部埋め込みやWYSIWYG出力を局所的に整えたいとき
たとえば記事カードの一覧では、h2やimgやaが何度も出てきます。そこで毎回.article-card__title-linkのような専用クラスを振るより、まずは.article-cardをルートにして必要な範囲だけ整えるほうが速いケースがあります。
また、Chrome for Developersの解説でも触れられているように、@scopeはspecificityの調整がしやすいのが強みです。ルート側のセレクタが重くても、内側のimgやa自体のspecificityは必要以上に上がりません。あとからテーマ上書きを入れるときも助かります。
応用コード
次は、「カード全体は整えたいが、本文内の図版だけは除外したい」というケースです。to (...)を使うと、途中の要素を境界にして対象範囲を切れます。
<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>@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構造へ強く依存しがちでした。@scopeのto (...)を使うと、「この範囲までは対象、ここから先は除外」という考え方で整理しやすくなります。
注意点
@scopeはスタイル隔離ではない
Chrome for Developersの解説でも強調されていますが、@scopeはセレクタの到達範囲を制御する機能です。継承プロパティはスコープの下限を越えて伝わることがあります。:scopeと&は同じではない:scopeはマッチしたスコープルート自身、&はルート指定セレクタの展開に近い動きをします。specificityの扱いも変わるので、読みやすさ優先で使い分けたほうが安全です。- 古い環境では代替セレクタを残す判断も必要
MDNの互換情報は必ず確認したいです。2026年3月31日時点では、関連するCSSScopeRuleはBaseline 2025として案内されていますが、案件によっては対象ブラウザ次第で段階導入が現実的です。 - クラス設計を完全に置き換えるものではない
再利用性の高いデザインシステムでは、クラス命名の明快さが必要な場面もあります。@scopeは「クラスを減らす魔法」ではなく、セレクタ設計を軽くする選択肢として使うのがちょうどいいです。
まとめ
@scopeは、CSSを激変させる派手な機能ではありませんが、既存コンポーネントの整理や局所的な上書きにかなり効きます。特に「長いセレクタは避けたいが、素の要素セレクタでは広すぎる」という中間地帯で使いやすいです。
いきなり全面採用するより、カードUIやリッチテキストの装飾、CMS出力の整形などから試すのがおすすめです。セレクタの責務が少し軽くなるだけで、あとから読むときの気楽さがかなり変わります。
ポイント
@scopeは、セレクタを長くしすぎずにコンポーネント内だけを狙いたいときに効くto (...)を使うと、図版や本文など一部だけ除外する設計がしやすい- スタイル隔離ではなくセレクタ制御なので、継承や対応ブラウザは別途意識しておく