Title: CSS Cascading and Inheritance Module Level 6 Shortname: css-cascade Level: 6 Status: ED Prepare for TR: no Work Status: Revising Group: csswg ED: https://drafts.csswg.org/css-cascade-6/ TR: https://www.w3.org/TR/css-cascade-6/ Previous Version: https://www.w3.org/TR/2021/WD-css-cascade-6-20211221/ Editor: Elika J. Etemad / fantasai, Apple, http://fantasai.inkedblade.net/contact, w3cid 35400 Editor: Miriam E. Suzanne, Invited Expert, http://miriamsuzanne.com/contact, w3cid 117151 Editor: Tab Atkins Jr., Google, http://xanthir.com/contact/, w3cid 42199 Abstract: This CSS module describes how to collate style rules and assign values to all properties on all elements. By way of cascading and inheritance, values are propagated for all properties on all elements. Abstract: Abstract: New in this level is [[#scoped-styles]]. Ignored Terms: auto, flex items,Informative Classes: ex
spec:dom; type:dfn; text:shadow tree for:tree; text:root text:shadow root; for:/ spec:dom; type:dfn; text:parent element spec:css-color-4; type:property; text:color spec:css-values-3; type: value; text:ex spec:css-conditional-3; type:at-rule; text:@media spec:mediaqueries-4; type:type; for:@media; text:all spec:mediaqueries-4; type:type; text:spec:selectors-4; type:dfn; text:subject spec:selectors-4; type:dfn; text:selector spec:selectors-4; type:dfn; text:combinator spec:html; type:element; text:style spec:css-scoping-1; type:dfn; text:shadow host
spec:mediaqueries-5 spec:css-values-4 spec:css-fonts-4
@scope (.light-scheme) {
/* Only match links inside a light-scheme */
a { color: darkmagenta; }
}
@scope (.dark-scheme) {
/* Only match links inside a dark-scheme */
a { color: plum; }
}
@scope (.media-object) {
/* Only match author images inside a media-object */
.author-image { border-radius: 50%; }
}
@scope (.media-object) to (.content > *) {
img { border-radius: 50%; }
.content { padding: 1em; }
}
The ''img'' selector will only match image tags that are in a DOM fragment
starting with any ''.media-object'',
and including all descendants up to
any intervening children of the ''.content'' class.
:where(:scope).
* The [=cascade=] prioritizes declarations
with a [=scope proximity|more proximate=] [=scoping root=],
regardless of specificity or order of appearance
by applying [=scope proximity=]
between the [=scoping root=] and the [=subject=]
of each [=scoped style rule=].
Note: Unlike Nesting,
selectors within an ''@scope'' rule
do not acquire the specificity of any parent selector(s) in the ''@scope'' prelude.
@scope (#hero) {
img { border-radius: 50%; }
}
:where(#hero) img { border-radius: 50%; }
The additional specificity of the ''#hero'' selector
is not applied to the specificity of the scoped selector.
However, since one <{img}> selector is scoped,
that selector is weighted more strongly in the cascade
with the application of [=scope proximity=].
main-component and sub-component)
and every element is marked as part of one or both scopes
using the data-scope attribute:
<section data-scope="main-component"> <p data-scope="main-component">...<p> <!-- sub-component root is in both scopes --> <section data-scope="main-component sub-component"> <!-- children are only in the inner scope --> <p data-scope="sub-component">...<p> </section> </section>Those custom scope attributes are then appended to every single selector in 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; }
Using the ''@scope'' rule,
authors and tools can replicate similar behavior
with the unique attribute or class
applied only to the [=scoping roots=]:
<section data-scope="main-component"> <p>...<p> <section data-scope="sub-component"> <p>...<p> </section> </section>Then the class or attribute can be used for establishing both upper and lower boundaries. Elements matched by a lower boundary selector are excluded from the resulting scope, which allows authors to create non-overlapping scopes by default:
@scope ([data-scope='main-component']) to ([data-scope]) {
p { color: red; }
/* only the outer section is part of the outer scope */
section { background: snow; }
}
@scope ([data-scope='sub-component']) to ([data-scope]) {
p { color: blue; }
/* the inner section is only part of the inner scope */
section { color: ghostwhite; }
}
However, authors can use the child combinator
and universal selector to create scope boundaries that overlap,
such that the inner scope root is part of both scopes:
@scope ([data-scope='main-component']) to ([data-scope] > *) {
p { color: red; }
/* both sections are part of the outer scope */
section { background: snow; }
}
@scope [(<where: * <>)]? [to (< >)]? { < > }
@scope (#my-component) {
p { color: green; }
:scope p { color: green; }
}
Authors can adjust the implied relationship
by adding an explicit combinator:
@scope (#my-component) {
> p { color: green; }
:scope > p { color: green; }
}
Authors can also target or explicitly position
the [=scoping root=] in a selector
by including either '':scope'' or ''&'' in a given selector:
@scope (#my-component) {
:scope { border: thin solid; }
& { border: thin solid; }
main :scope p { color: green; }
main & p { color: green; }
}
Both the '':scope'' and ''&'' selectors
match the [=scope root=],
but with different [=specificity=]:
'':scope'' has as [=specificity=] of (0,1,0),
whereas ''&'' has a [=specificity=] of 0.
<div>
<style>
@scope {
p { color: red; }
}
</style>
<p>this is red</p>
</div>
<p>not red</p>
That would be equivalent to:
<div id="foo">
<style>
@scope (#foo) {
p { color: red; }
}
</style>
<p>this is red</p>
</div>
<p>not red</p>
/* .content is only a limit when it is a direct child of the :scope */
@scope (.media-object) to (:scope > .content) { ... }
[=Scoping limits=] can also reference elements outside their [=scoping root=]
by using '':scope''.
For example:
/* .content is only a limit when the :scope is inside .sidebar */
@scope (.media-object) to (.sidebar :scope .content) { ... }
@scope (.parent-scope) {
@scope (:scope > .child-scope) to (:scope .limit) {
:scope .content {
color: red;
}
}
}
is equivalent to:
@scope (.parent-scope > .child-scope) to (.parent-scope > .child-scope .limit) {
.parent-scope > .child-scope .content {
color: red;
}
}
@scope (.foo) {
border: 1px solid black;
}
is equivalent to:
@scope (.foo) {
:where(:scope) {
border: 1px solid black;
}
}
CSSScopeRule interface
[Exposed=Window]
interface CSSScopeRule : CSSGroupingRule {
readonly attribute CSSOMString? start;
readonly attribute CSSOMString? end;
};
start of type CSSOMString
start attribute
returns the result of serializing the <end of type CSSOMString
end attribute
returns the result of serializing the <:where(:scope).
(Issue 9740)
* Allowed [=declarations=] directly within ''@scope''.
(Issue 10389)
* The '':scope'' selector can match the [=featureless=] [=shadow host=] when
that host is the [=scoping root=] element.
(Issue 9025)
* ''<Content-Type metadata
(or any same-origin file if the host document is in quirks mode)
are text/css,
potentially allowing arbitrary files to be imported into the page
and interpreted as CSS,
potentially allowing sensitive data to be inferred from the computed styles they apply to a document.