Title: CSS Cascading and Inheritance 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:mediaqueries-5
spec:css-values-4
spec:css-fonts-4

Introduction and Missing Sections

Issue: This is a diff spec over CSS Cascading and Inheritance Level 5. It is currently an Exploratory Working Draft: if you are implementing anything, please use Level 5 as a reference. We will merge the Level 5 text into this draft once it reaches CR.

Cascading

The cascade takes an unordered list of declared values for a given property on a given element, sorts them by their [=declaration’s=] precedence as determined below, and outputs a single cascaded value.

Cascade Sorting Order

The cascade sorts [=declarations=] according to the following criteria, in descending order of precedence:
Origin and Importance
The origin of a [=declaration=] is based on where it comes from and its importance is whether or not it is declared with ''!important'' (see [[#importance|below]]). The precedence of the various origins is, in descending order:
  1. Transition declarations [[!css-transitions-1]]
  2. [=Important=] [=user-agent origin|user agent=] declarations
  3. [=Important=] [=user origin|user=] declarations
  4. [=Important=] [=author origin|author=] declarations
  5. Animation declarations [[!css-animations-1]]
  6. [=Normal=] [=author origin|author=] declarations
  7. [=Normal=] [=user origin|user=] declarations
  8. [=Normal=] [=user-agent origin|user agent=] declarations
Declarations from origins earlier in this list win over declarations from later origins.
Context
A document language can provide for blending [=declarations=] sourced from different encapsulation contexts, such as the nested [=tree contexts=] of [=shadow trees=] in the [[!DOM]]. When comparing two declarations that are sourced from different [=encapsulation contexts=], then for [=normal=] rules the declaration from the outer context wins, and for [=important=] rules the declaration from the inner context wins. For this purpose, [[DOM]] [=tree contexts=] are considered to be nested in [=shadow-including tree order=]. Note: This effectively means that [=normal=] declarations belonging to an [=encapsulation context=] can set defaults that are easily overridden by the outer context, while [=important=] declarations belonging to an [=encapsulation context=] can enforce requirements that cannot be overridden by the outer context.
The Style Attribute
Separately for [=normal=] and [=important=] [=declarations=], declarations that are attached directly to an element (such as the contents of a style attribute) rather than indirectly mapped by means of a style rule selector take precedence over declarations the same [=importance=] that are mapped via style rule.
Layers
[=Declarations=] within each [=origin=] and [=context=] can be explicitly assigned to a [=cascade layer=]. For the purpose of this step, any declaration not assigned to an explicit layer is added to an implicit final layer. Cascade layers (like declarations) are sorted by order of appearance, see [[#layer-ordering]]. When comparing declarations that belong to different layers, then for [=normal=] rules the declaration whose [=cascade layer=] is latest in the layer order wins, and for [=important=] rules the declaration whose [=cascade layer=] is earliest wins. Note: This follows the same logic used for precedence of [=normal=] and [=important=] [=origins=], thus the ''!important'' flag maintains the same “override” purpose in both settings.
Specificity
The Selectors module [[!SELECT]] describes how to compute the specificity of a selector. Each declaration has the same specificity as the style rule it appears in. The declaration with the highest specificity wins.
Scope Proximity
When comparing declarations that appear in style rules with different [=scoping roots=], then the declaration with the fewest generational or sibling-element hops between the [=scoping root=] and the [=scoped style rule=] [=subject=] wins. For this purpose, style rules without a [=scoping root=] are considered to have infinite proximity hops.
Order of Appearance
The last declaration in document order wins. For this purpose:
The output of the cascade is a (potentially empty) sorted list of declared values for each property on each element.

Cascading Origins

Issue: [[css-cascade-5#cascading-origins]] cascade origin

Important Declarations: the ''!important'' annotation

Issue: [[css-cascade-5#importance]] important normal

Cascade Layers

Issue: [[css-cascade-5#layering]]

Layer Ordering

Issue: [[css-cascade-5#layer-ordering]]

Scoping Styles: the ''@scope'' rule

A scope is a subtree or fragment of a document, which can be used by selectors for more targeted matching. A [=scope=] is formed by determining: * The [=scoping root=] [=node=], which acts as the upper bound of the scope, and optionally: * The scoping limit elements, which act as the lower bounds. An element is in scope if: * It is an [=inclusive descendant=] of the [=scoping root=], and * It is not an [=inclusive descendant=] of a [=scoping limit=]. Note: In contrast to [[CSS-SCOPING-1#shadow-dom|Shadow Encapsulation]], 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. Scoped styles are described in CSS using the @scope [=block at-rule=], which declares a [=scoping root=] and optional [=scoping limits=] associated with a set of [=style rules=].
For example, an author might have wide-reaching color-scheme scopes, which overlap more narrowly-scoped design patterns such as a media object. The selectors in the ''@scope'' rule establish [=scoping root=] and optional [=scoping limit=] elements, while the nested selectors only match elements that are [=in scope|in a resulting scope=]:
		@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%; }
		}
		
By providing [=scoping limits=], an author can limit matching more deeply nested descendants. For example:
		@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.
Issue: Should scoping limits be added to the definition of [=scoped selectors=]?

Effects of ''@scope''

The ''@scope'' [=at-rule=] has three primary effects on the [=style rules=] it contains: * The [=style rules=] in an ''@scope'' <> are [=scoped style rules=]. * The '':scope'' selector is defined to match the ''@scope'' rule’s [=scoping root=]. The ''&'' selector is defined to represent the selector representing the [=scoping root=] (the <> selector), or else '':scope'' if no selector was specified. * 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.
The following selectors have the same specificity (0,0,1):
		@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=].
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 (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; }
		}
		

Syntax of ''@scope''

The syntax of the ''@scope'' rule is:
	@scope [(<>)]? [to (<>)]? {
	  <>
	}
	
where: * <> is a <> [=selector=] used to identify the [=scoping root=](s). * <> is a <> [=selector=] used to identify any [=scoping limits=]. * the <> represents the [=scoped style rules=]. [=Pseudo-elements=] cannot be [=scoping roots=] or [=scoping limits=]; they are invalid both within <> and <>.

Scoped Style Rules

Scoped style rules differ from non-scoped rules in the following ways: * Their selectors can only match elements that are [=in scope=]. (This only applies to the [=subject=]; the rest of the selector can match unrestricted.) * They accept a <> as their prelude (rather than just a <>). Such [=relative selectors=] are relative to '':scope''. * Any selector in the <> that does not start with a [=combinator=] but does [=contain the nesting selector=] or the '':scope'' selector, is interpreted as a non-[=relative selector=] (but the [=subject=] must still be [=in scope=] to match).
By default, selectors in a [=scoped style rule=] are [=relative selectors=], with the [=scoping root=] and [=descendant combinator=] implied at the start. The following selectors will match the same elements:
		@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; }
		}
		
While the '':scope'' or ''&'' selectors can both refer to the [=scoping root=], they have otherwise different meanings in this context: : Differences in selector matching :: The '':scope'' selector will only match the [=scoping root=] itself, while the ''&'' selector is able to match any element that is matched by the <> selector list. : Differences in selector specificity :: The '':scope'' selector has a specificity equal to other pseudo-classes, while the ''&'' selector has the specificity equal to the most specific selector in <>.

Identifying Scoping Roots and Limits

A ''@scope'' rule produces one or more [=scopes=] as follows: : Finding the [=scoping root=](s) :: For each element matched by <>, create a [=scope=] using that element as the [=scoping root=]. If no <> is specified, the [=scoping root=] is the [=parent element=] of the [=owner node=] of the stylesheet where the ''@scope'' rule is defined. (If no such element exists, then the [=scoping root=] is the [=root=] of the containing [=node tree=].) Any '':scope'' or ''&'' selectors in <> are interpreted as defined for its outer context. : Finding any [=scoping limits=] :: For each [=scope=] created by a [=scoping root=], its [=scoping limits=] are set to all elements that are [=in scope=] and that match <>, interpreting '':scope'' and ''&'' exactly as in [=scoped style rules=].
Authors can establish local scoping for <{style}> elements by leaving out the <> selector. For example:
		<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>
		
[=Scoping limits=] can use the '':scope'' pseudo-class to require a specific relationship to the [=scoping root=]:
		/* .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 Nesting

''@scope'' rules can be nested. In this case, just as with the nested style rules, the prelude selectors of the inner ''@scope'' (those defining its [=scope=]) are [=scoped selectors|scoped by=] the selectors of the outer one. Note: The resulting [=scope=] for further nested [=scoped style rules=] is practically constrained by both the outer and inner ''@scope'' rules, but the [=scoping root=] is defined by the innermost ''@scope''. Since [=scope proximity=] is measured between a [=scoped style rule=] [=subject=] and [=scoping root=], only the innermost ''@scope'' matters for determining [=scope proximity=] of [[#scope-scope|nested @scope rules]].
When nesting ''@scope'' rules inside other ''@scope'' rules, or inside other selectors, the <> selector is [=relative selector|relative to=] the nesting context, while the <> and any [=scoped style rules=] are [=relative selector|relative to=] the [=scoping root=] For example, the following code:
		@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;
		  }
		}
		
Global name-defining [=at-rules=] such as ''@keyframes'' or ''@font-face'' or ''@layer'' that are defined inside ''@scope'' are valid, but are not scoped or otherwise affected by the enclosing ''@scope'' rule. However, any [=style rules=] contained by such rules (e.g. within ''@layer'') are [=scoped style rules|scoped=].

Precedence of Non-CSS Presentational Hints

Issue: [[css-cascade-5#layering]] CSSOM {#cssom} ==============

The CSSScopeRule interface

The {{CSSScopeRule}} interface represents the ''@scope'' rule:
	[Exposed=Window]
	interface CSSScopeRule : CSSGroupingRule {
	  readonly attribute CSSOMString? start;
	  readonly attribute CSSOMString? end;
	};
	
start of type CSSOMString
The start attribute returns the result of serializing the <> of the rule (without the enclosing parentheses), or null if there is no <>.
end of type CSSOMString
The end attribute returns the result of serializing the <> of the rule (without the enclosing parentheses), or null if there is no <>.

Changes

This appendix is informative.

Changes since the 21 March 2023 Working Draft

Significant changes since the 21 March 2023 Working Draft include: * Clarified that [=scope proximity=] is a single measurement of the steps between a single [=scoping root=] and [=scoped style rule=] [=subject=]. * Removed strong scope proximity. (Issue 6790) * Removed the scoped descendant combinator (deferred). (Issue 8628) * Added the {{CSSScopeRule}} interface. (Issue 8626)

Changes since the 21 December 2021 First Public Working Draft

Significant changes since the 21 December 2021 First Public Working Draft include: * Clarified ''@scope'' effects on nested '':scope'' and ''&'' selectors. (Issue 8377) * Removed ''@scope'' prelude from specificity calculation. (Issue 8500) * Specified how name-defining [=at-rules=] behave in ''@scope''. (Issue 6895) * Added implicit scopes by making ''<>'' optional. (Issue 6606) * Disallowed [=pseudo-elements=] in the ''@scope'' prelude. (Issue 7382) * Removed selector scoping notation. (Issue 7709) * [=Scoping limit=] elements are excluded from the resulting [=scope=]. (Issue 6577)

Additions Since Level 5

The following features have been added since Level 5: * The definition of a [=scope=], as described by a combination of <> and <> 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

Additions Since Level 4

The following features have been added since Level 4: * Added [=cascade layers=] to the [=cascade=] sort criteria (and defined style attributes as a distinct step of the [=cascade=] sort criteria so that they interact appropriately). * Introduced the ''@layer'' rule for defining cascade layers. * Added ''layer''/''layer()'' option to ''@import'' definition. * Introduced the ''revert-layer'' keyword for rolling back values to previous layers.

Additions Since Level 3

The following features have been added since Level 3: * Introduced ''revert'' keyword, for rolling back the cascade. * Introduced ''supports()'' syntax for supports-conditional ''@import'' rules. * Added [=encapsulation context=] to the [=cascade=] sort criteria to accommodate Shadow DOM. [[DOM]] * Defined the property two aliasing mechanisms CSS uses to support legacy syntaxes. See [[css-cascade-4#aliasing]].

Additions Since Level 2

The following features have been added since Level 2:
  • The 'all' shorthand
  • The ''initial'' keyword
  • The ''unset'' keyword
  • Incorporation of animations and transitions into the cascade.

Acknowledgments

David Baron, Tantek Çelik, Keith Grant, Giuseppe Gurgone, Theresa O'Connor, Florian Rivoal, Noam Rosenthal, Simon Sapin, Jen Simmons, Nicole Sullivan, Lea Verou, and Boris Zbarsky contributed to this specification.

Privacy Considerations

* User preferences and UA defaults expressed via application of style rules are exposed by the cascade process, and can be inferred from the computed styles they apply to a document.

Security Considerations

* The cascade process does not distinguish between same-origin and cross-origin stylesheets, enabling the content of cross-origin stylesheets to be inferred from the computed styles they apply to a document. * The ''@import'' rule does not apply the [=CORS protocol=] to loading cross-origin stylesheets, instead allowing them to be freely imported and applied. * The ''@import'' rule assumes that resources without 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.