スタイルの適用範囲を限定する CSS の `@scope` ルール
`@scope` アットルールは特定のセレクタの範囲に限定したスタイルを適用するためのルールです。`@scope` のルールセットに 1 つの CSS セレクタを指定すると、そのセレクタがスコープのルートとなります。`@scope` ルール内のスタイルはそのセレクタの範囲内でのみ適用されます。
CSS の @scope
アットルールは特定のセレクタの範囲に限定したスタイルを適用するためのルールです。@scope
ルールは以下のように記述します。
@scope (.card) {
p {
color: red;
}
}
@scope()
はルールセットとして CSS のセレクタを指定します。例えば @scope(main)
や @scope(.container)
といった感じです。ルールセットで指定したセレクタがスコープのルートとなり、@scope
ルール内のスタイルはそのセレクタの範囲内でのみ適用されます。
上記のコード例では、.card
クラスが付与された要素内の p
要素にのみ color: red
が適用されます。それ以外の p
要素には適用されません。
@scope
の基本構文
記事の冒頭でも触れたように、@scope
のルールセットに 1 つの CSS セレクタを指定すると、そのセレクタがスコープのルートとなります。下記のコード例では、article
要素内にのみ適用されるスタイルを指定しています。
@scope (article) {
/**
}
@scope
のリミット
to
を使用してスコープのリミットを指定することもできます。スコープのリミットは範囲の下限として機能します。例えば、次のような HTML があるとしましょう。
<div class="container">
<p>text 1</p>
<div class="inner">
<p>text 2</p>
</div>
</div>
@scope
ルールでは .container
要素をスコープのルートとし、.inner
要素をスコープのリミットとして指定します。
@scope (.container) to (.inner) {
p {
color: red;
}
}
この場合 .inner
要素は .container
の範囲内に含まれていますが、同時に .inner
要素がスコープのリミットとして指定されているため、.inner
要素内の p
要素には p { color: red; }
が適用されません。
スコープの下限にスコープのリミット自身を含めるかどうかは議論がありました。スコープのリミット自身を含めたい場合には @scope (.container) to (.inner > *)
のように指定することで対応できることから、スコープのリミットを含めない仕様となりました。https://github.com/w3c/csswg-drafts/issues/6577
@scope
ルール
インラインスタイル HTML 内で <style>
要素を使用してインラインスタイルとして @scope
ルールを記述できます。その場合、@scope
のルールセットの指定は省略され、<style>
要素の親要素がスコープのルートとなります。なお、スコープのリミットはルートは自動で設定されません。
<main class="main">
<div class="container">
<style>
@scope {
p {
color: red;
}
}
</style>
<p>text 1</p>
<div class="inner">
<p>text 2</p>
</div>
</div>
<p>test 3</p>
</main>
上記のコード例では、<style>
の親要素である .container
要素がスコープのルートとなり、.container
要素内の p
要素にのみ color: red
が適用されます。
:scope
擬似クラス
@scope
ブロック内で :scope
擬似クラスを使用できます。:scope
擬似クラスはスコープのルート要素を指定するために使用されます。つまり、scope(.card)
ルール内での :scope
は .card
要素を指すということです。
@scope (.card) {
:scope {
border: 1px solid #e0e0e0;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
max-width: 300px;
padding: 16px;
}
h2 {
font-size: 1.5rem;
font-weight: bold;
margin: 0;
}
p {
color: #333;
}
}
@scope
の競合の解決
ある要素が複数の @scope
ルールに含まれる場合、どちらのスタイルが適用されるのでしょうか?以下のように入れ子の構造の HTML があるとします。
<div class="section1">
<div class="section2">
<p>text</p>
</div>
</div>
そして次の CSS では .section1
と .section2
それぞれに @scope
ルールが適用されています。
@scope (.section1 .section2) {
p {
color: yellow;
}
}
@scope (.section2) {
p {
color: blue;
}
}
@scope (.section1) {
p {
color: red;
}
}
このとき、p
要素には blue
と red
のどちらの色が適用されるでしょうか?正解は .section2
の @scope
ルールによって color: blue
です。
@scope
ルール内では従来の CSS のカスケードとは異なる基準が適用されます。CSS の通常のカスケードでは、詳細度がより高いスタイルが、同じ詳細度の場合は後に記述されたスタイルが適用されました。@scope
ルール内ではスコープルートまでの距離が最も近い @scope
ルールが適用されます。これをスコープの近接性と呼びます。
この例では p
要素に最も近い .section2
要素の @scope
ルールが適用されます。なお、@scope
のルールセットとして渡しているセレクタは優先度の計算に使用されません。そのため、.section1 .section2
というセレクタは .section2
というセレクタよりも詳細度が高いということにはならないため、より後ろに記述された .section2
の @scope
ルールが適用されます。
なお、スコープの近接性のルールはスタイルの記述順よりも優先して適用されるものの、スタイルの詳細度や !important
、@layer
ルールなどより強い優先度を持つものには勝つことができないことに注意してください。以下の例では .section2
の @scope
ルールのほうが近接しているにもかかわらず、.section1
の @scope
ルールで使用されているスタイルの詳細度がより高いため、color: red
が適用されます。
@scope (.section2) {
p {
color: blue;
}
}
@scope (.section1) {
:scope {
p {
color: red;
}
}
}
それでは @scope
ルール内のスタイルと、通常の CSS のスタイルとの優先度の競合の場合にはどうなるでしょうか?
@scope (section) {
p {
color: blue;
}
}
p {
color: red;
}
この場合にはどちらも同じ詳細度(0-0-1
)を持ちますが、@scope
ルール内のスタイルが優先して適用されます。
@scope
の詳細度
@scope
ルールのルールセットで使用されているセレクタは、@scope
ブロック内のセレクタに影響を与えないという特徴があります。以下の例における p
要素は .card
要素内にのみ適用されるスタイルを指定していますが、詳細度は 0-0-1
となっています。
@scope (.card) {
p {
/* 詳細度 0-0-1 */
color: red;
}
}
/** スコープのルールセットのセレクタは影響しない */
@scope (.card p) {
p {
/* 詳細度 0-0-1 */
color: blue;
}
}
@scope (#card) {
p {
/* 詳細度 0-0-1 */
color: green;
}
}
まとめ
@scope
ルールは特定のセレクタの範囲に限定したスタイルを適用するためのルール@scope
のルールセットに 1 つの CSS セレクタを指定すると、そのセレクタがスコープのルートとなる@scope
ルール内でto
を使用してスコープのリミットを指定することができる@scope
ルール内で:scope
擬似クラスを使用するとスコープのルート要素を指定できる@scope
ルール内では新たなカスケードのルールとしてスコープの近接性が適用される。スコープの近接性はスコープルートまでの距離が最も近い@scope
ルールが適用される@scope
ルール内のスタイルと通常の CSS のスタイルとの優先度の競合の場合、@scope
ルール内のスタイルが優先して適用される