FFFF csswg-drafts/css-scoping-2/Overview.bs at fdffbb36d96e677772dcf3537f2c52e381132b08 · w3c/csswg-drafts · GitHub
Skip to content

Latest commit

 

History

History
441 lines (356 loc) · 15.4 KB

File metadata and controls

441 lines (356 loc) · 15.4 KB
<pre class='metadata'>
Title: CSS Scoping Module Level 2
Level: 2
Shortname: css-scoping
Group: CSSWG
Status: ED
Work Status: Exploring
TR: https://www.w3.org/TR/css-scoping-1/
ED: https://drafts.csswg.org/css-scoping/
Editor: Tab Atkins Jr., Google, http://xanthir.com/contact/, w3cid 42199
Editor: Miriam E. Suzanne, Invited Expert, http://miriamsuzanne.com/contact, w3cid 117151
Abstract: This specification defines scoping/encapsulation mechanisms for CSS.
Ignored Terms: inherit, slot, custom elements, stylesheets
Ignored Vars: root elements
</pre>
<pre class="link-defaults">
spec:dom; type:dfn;
text:host
text:shadow root; for:/
text:root; for:tree
text:find slottables
text:find flattened slottables
text:element; for:/
spec:html; type:element; text:style
spec:selectors-4; type:dfn;
text: static profile
text: dynamic profile
spec:css-pseudo-4; type:selector;
text:::before
text:::after
spec:cascade-4; type:dfn; text: inherit
</pre>
<h2 id="intro">
Introduction</h2>
Issue: This is a diff spec over <a href="https://www.w3.org/TR/css-scoping-1/">CSS Scoping Module Level 1</a>.
It is currently an Exploratory Working Draft:
if you are implementing anything, please use Level 1 as a reference.
We will merge the Level 1 text into this draft once it reaches CR.
<!--
████████ ████████ ████████ ███ ██ ██ ██ ████████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██████ ██████ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ █████████ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██
████████ ████████ ██ ██ ██ ███████ ████████ ██
-->
<h2 id='default-element-styles'>
Default Styles for Custom Elements</h2>
Issue: [[css-scoping-1#default-element-styles]]
<!--
██████ ██ ██ ███ ████████ ███████ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██████ █████████ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ █████████ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██████ ██ ██ ██ ██ ████████ ███████ ███ ███
-->
<h2 id='shadow-dom'>
Shadow Encapsulation</h2>
Issue: [[css-scoping-1#shadow-dom]]
<!--
██████ ██████ ███████ ████████ ████ ██ ██ ██████
██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ████ ██ ██
██████ ██ ██ ██ ████████ ██ ██ ██ ██ ██ ████
██ ██ ██ ██ ██ ██ ██ ████ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ ██
██████ ██████ ███████ ██ ████ ██ ██ ██████
-->
<h2 id='scoped-styles'>
Scoped Styles</h2>
A <dfn>scope</dfn> is a subtree or fragment of a document,
which can be used by selectors for more targeted matching.
[=Scopes=] are described in CSS through a combination of two selector lists:
* The <dfn><<scope-start>></dfn> is a <<forgiving-selector-list>>.
Each element matched by <<scope-start>> is a [=scoping element=],
creating a scope with itself as the [=scoping root=].
* The <dfn><<scope-end>></dfn> is a <<forgiving-selector-list>>
that is [=scoped selector|scoped=] by the <<scope-start>> selector,
with the [=scoping roots=] as [=:scope elements=].
Each element matched by <<scope-end>> is a [=scope boundary=].
<dfn>Scope boundary</dfn> elements provide lower bounds to a scope,
so that [=scoped selectors=] are not able to match more deeply nested elements.
Note: This means that the '':scope'' pseudo-class
in a lower boundary (<<scope-end>>) selector
will always refer to the [=scope=] being described,
rather than the outer scope context.
<div class=example>
For example,
[=scope boundaries=] can be contextual:
<pre class=lang-css>
/* .content is only a boundary when the :scope is inside .sidebar */
@scope (.media-object) to (.sidebar :scope .content) {
img { border-radius: 50%; }
}
</pre>
Or based on a specific relationship to the [=scoping root=]:
<pre class=lang-css>
/* .content is only a boundary when it is a direct child of the :scope */
@scope (.media-object) to (:scope > .content) {
img { border-radius: 50%; }
}
</pre>
</div>
Each resulting [=scope=] includes a [=scoping root=] and all its descendants,
up to and including any [=scope boundary=] elements,
but not the descendants of those boundaries.
In contrast to <a href="#shadow-dom">Shadow Encapsulation</a>,
which describes a persistent one-to-one relationship in the DOM
between a [=shadow host=] and its nested [=shadow tree=],
multiple overlapping [=scopes=] can be defined in relation to the same elements.
<div class=example>
For example,
An author might have wide-reaching color-scheme scopes,
which overlap more narrowly defined design patterns
such as a media object:
<pre class=lang-css>
@scope (.light-scheme) {
a { color: darkmagenta; }
}
@scope (.dark-scheme) {
a { color: plum; }
}
@scope (.media-object) {
.media-image { border-radius: 50%; }
.media-content { padding: 1em; }
}
</pre>
</pre>
</div>
<div class=example>
By providing [=scope boundaries=],
an author can limit matching more deeply nested descendants.
For example:
<pre class=lang-css>
@scope (.media-object) to (.content) {
img { border-radius: 50%; }
/* it is also possible to style the lower boundary itself */
.content { padding: 1em; }
}
</pre>
The ''img'' selector will only match image tags that are inside a ''.media-object'',
without any intervening ''.content'' class between it and the [=scoping root=].
</div>
<!--
██ ████ ██ ██
████ ██ ███ ██
██ ██ ████ ██
██ ██ ██ ██
██ ██ ██ ████
████ ██ ██ ███
██ ████ ██ ██
-->
<h3 id='in-scope-selector'>
The in-scope pseudo-class '':in()''</h3>
The in-scope pseudo-class, <dfn>:in()</dfn>,
is a functional pseudo-class with the following syntax:
<pre class='prod'>
:in(<<scope-start>> [/ <<scope-end>>]?)
</pre>
If, after parsing, <<scope-start>> is an empty list,
the pseudo-class is valid but matches nothing, and defines no scopes.
Otherwise, the pseudo-class matches any element that is in a [=scope=]
described by the given <<scope-start>> and <<scope-end>> selectors.
Note: This does not effect the [=:scope elements=] for the selector.
The specificity of the '':in()'' pseudo-class
is replaced by the specificity of
the most specific [=complex selector=] in its <<scope-start>> argument.
<div class=example>
The purpose of the in-scope pseudo-class
is to allow adding [=scope boundaries=] to a selector:
<pre class=lang-css>
.title:in(.post / .comments) { font-size: 2em; }
</pre>
Without any such lower boundaries,
the ''in()'' pseudo-class is similar to other existing selectors.
These three selectors will all select the same elements,
with the same specificity:
<pre class=lang-css>
.child:in(.ancestor) { color: darkmagenta; }
.child:is(.ancestor, .ancestor *) { color: darkmagenta; }
.ancestor.child, .ancestor .child { color: darkmagenta; }
</pre>
</div>
<!--
███████ ██████ ██████ ███████ ████████ ████████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ███ ██ ██ ██ ██ ██ ██ ██ ██
██ ███ ██ ██████ ██ ██ ██ ████████ ██████
██ █████ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██
███████ ██████ ██████ ███████ ██ ████████
-->
<h3 id='scope-atrule'>
Scoping Styles in CSS: the ''@scope'' rule</h3>
The <dfn at-rule id="at-ruledef-layer">@scope</dfn> [=block at-rule=]
allows authors to create scoped stylesheets in CSS,
with the addition of [=scope proximity=] weighting in the cascade.
The syntax of the ''@scope'' rule is:
<pre class='prod'>
@scope (<<scope-start>>) [to (<<scope-end>>)]? {
<<stylesheet>>
}
</pre>
The ''@scope'' rule has three primary effects on the style rules in <<stylesheet>>.
For each [=scope=] that is described by the given <<scope-start>> and <<scope-end>>:
* [=scoped selectors|Selectors are scoped=] to the [=scope=] in question,
with the [=:scope element=] being the [=scoping root=].
* Selectors are given the added specificity of
the most specific [=complex selector=] in the <<scope-start>> argument.
This is designed to match the behavior of the '':in()'' and '':is()'' selectors.
* The cascade prioritizes rules with a [=scope proximity|more proximate=] [=scoping root=],
regardless of source order.
<div class=example>
The following selectors have the same specificity (1,0,1):
<pre class=lang-css>
#hero img { border-radius: 50%; }
@scope (#hero) {
img { border-radius: 50%; }
}
</pre>
</div>
<div class=example>
Many existing tools implement "scoped styles"
by applying a unique class or attribute
to every element in a given scope
or "single file component."
In this example there are two scopes
(<code>main-component</code> and <code>sub-component</code>)
and every element is marked as part of one or both scopes
using the <code>data-scope</code> attribute:
<pre class=lang-html>
&lt;section data-scope="main-component">
&lt;p data-scope="main-component">...&lt;p>
&lt;!-- sub-component root is in both scopes -->
&lt;section data-scope="main-component sub-component">
&lt;!-- children are only in the inner scope -->
&lt;p data-scope="sub-component">...&lt;p>
&lt;/section>
&lt;/section>
</pre>
Those custom scope attributes are then
appended to every single selector in CSS:
<pre class=lang-css>
p[data-scope~='main-component'] { color: red; }
p[data-scope~='sub-component'] { color: blue; }
/* both sections are part of the outer scope */
section[data-scope~='main-component'] { background: snow; }
/* the inner section is also part of the inner scope */
section[data-scope~='sub-component'] { color: ghostwhite; }
</pre>
Using the ''@scope'' rule,
authors and tools can replicate similar behavior
with the unique attribute or class
applied only to the [=scoping roots=]:
<pre class=lang-html>
&lt;section data-scope="main-component">
&lt;p>...&lt;p>
&lt;section data-scope="sub-component">
&lt;!-- children are only in the inner scope -->
&lt;p>...&lt;p>
&lt;/section>
&lt;/section>
</pre>
Then the class or attribute can be used
for establishing both upper and lower boundaries,
such that scopes only overlap at those boundaries:
<pre class=lang-css>
@scope ([data-scope='main-component']) to ([data-scope]) {
p { color: red; }
/* both sections are part of the outer scope */
section { background: snow; }
}
@scope ([data-scope='sub-component']) to ([data-scope]) {
p { color: blue; }
/* the inner section is also part of the inner scope */
section { color: ghostwhite; }
}
</pre>
</div>
''@scope'' rules can be nested.
In this case, just as with the nested style rules,
the selectors of the inner ''@scope'' are [=scoped selectors|scoped by=]
the selectors of the outer one.
<h4 id='scope-cascade'>
Scope Proximity in the Cascade</h3>
Issue: This likely belongs in the css-cascade specification.
<dfn>Scope proximity</dfn> is considered in the cascade sort order
after specificity, and before order of appearance.
If the [=:scope elements=] of two declarations
have an ancestor/descendant relationship,
then the declaration whose [=:scope element=] is the descendant wins.
Note: When the [=:scope element=] is not otherwise defined for a declaration,
it is the document root element.
<div class=example>
When scoped declarations overlap—
applying to the same elements
with the same cascade origin, importance, layer, and specificity—
then one with closer [=scope proximity=] takes precedence.
For example, this ''light-scheme'' and ''dark-scheme'' scopes:
<pre class=lang-css>
@scope (.light-scheme) {
a { color: darkmagenta; }
}
@scope (.dark-scheme) {
a { color: plum; }
}
</pre>
If ''light-scheme'' and ''dark-scheme'' classes are nested in the DOM,
whichever is closer to a given link in the DOM tree
will take precedence for styling that link,
regardless of their order of appearance in CSS:
<pre class=lang-html>
&lt;section class="light-scheme">
&lt;a href="#">
light scope:
darkmagenta link color
&lt;/a>
&lt;aside class="dark-scheme">
&lt;a href="#">
both scopes, but dark-scheme is a closer ancestor:
plum link color
&lt;/a>
&lt;/aside>
&lt;/section>
</pre>
</div>
<h2 id="changes">
Changes</h2>
<h3 id="additions-l">
Additions Since Level 1</h3>
The following features have been added since
<a href="https://www.w3.org/TR/css-scoping-1/">Level 1</a>:
* The definition of a [=scope=],
as described by a combination of <<scope-start>> and <<scope-end>> selectors.
* The in-scope ('':in()'') pseudo-class for selecting with lower-boundaries
* The ''@scope'' rule for creating scoped stylesheets
* The definition of [=scope proximity=] in the cascade
<h2 class="no-num" id="acknowledgments">Acknowledgments</h2>
Elika J. Etemad / fantasai,
Giuseppe Gurgone,
Keith Grant,
Lea Verou,
Nicole Sullivan,
and Theresa O'Connor
contributed to this specification.
Privacy and Security Considerations {#priv-sec}
===============================================
This specification introduces Shadow DOM and some shadow-piercing capabilities,
but this does not introduce any privacy or security issues--
shadow DOM, as currently specified, is intentionally not a privacy/security boundary
(and the parts of the UA that use shadow DOM and <em>do</em> have a privacy/security boundary
implicitly rely on protections not yet specified,
which protect them from the things defined in this specification).