Cascade & scope

The at-rules that bring other style sheets in, put rules into cascade layers that win or lose as a group, declare XML namespaces for selectors, and limit a set of rules to one part of the document. Media and feature conditions are under Conditional rules; the pseudo-classes used with them, such as :scope, are under Selectors.

Importing & layering

@import

Loads another style sheet and treats its rules as if they were written in place of the @import. It must come before every other rule in the sheet except @charset and @layer statements; an @import placed later is ignored.

Syntax@import [ <url> | <string> ] [[ layer | layer( <layer-name> ) ] || [ scope | scope( <scope-start> | <scope-boundaries> ) ] || <supports-import-condition>]? <media-import-condition> ;
SupportChrome 1 · Firefox 1 · Safari 1
Speccss-cascade-6
ConditionEffectSupport
layer / layer(<layer-name>)Puts the imported rules into a new anonymous layer, or into the named layer.Chrome 99 · Firefox 97 · Safari 15.4
supports(…)Imports the sheet only if the browser supports the declaration or condition, as in @supports.Chrome 122 · Firefox 115 · Safari 17.5
Media query listImports the sheet only when the media query matches.Chrome 1 · Firefox 1 · Safari 1
@import url("reset.css") layer(reset);
@import url("grid.css") layer(layout) supports(display: grid) screen;
Import into named layers, the second only where grid is supported and on screens.

Each @import is a separate request that the browser cannot start until it has read the importing sheet, so a chain of imports loads slowly. Bundling or several <link> elements avoid the wait.

@layer

Declares a cascade layer. Layers decide which rules win before specificity does: a rule in a later layer beats a rule in an earlier one however specific the earlier selector is. Styles in no layer beat every layer.

Syntax@layer <layer-name>? { <rule-list> } | @layer <layer-name>#;
SupportChrome 99 · Firefox 97 · Safari 15.4
Speccss-cascade-5

The rule has two forms. The statement form, @layer a, b, c;, only declares names, which fixes the order of the layers at the point they first appear. The block form, @layer name { … }, adds rules to a layer, creating it if it does not exist yet. A block with no name creates an anonymous layer that nothing else can add to. A <layer-name> may be dotted, as in components.buttons, to name a layer nested inside another.

/* fix the order first: later names win */
@layer reset, base, components;

@layer components {
  .button { color: white; }
}

@layer base {
  #main a { color: blue; }
}

a.button { color: black; } /* unlayered: beats both */
The order statement makes components beat base, even though base comes later in the file and has the stronger selector.

For !important declarations the order is reversed: an important declaration in an earlier layer beats one in a later layer, and both beat important unlayered styles.

Namespaces & scope

@namespace

Declares an XML namespace for the style sheet, either as the default for type selectors or bound to a prefix used as prefix|name. It matters only for documents that mix namespaces, such as SVG or MathML served as XML.

Syntax@namespace <namespace-prefix>? [ <string> | <url> ] ;
SupportChrome 1 · Firefox 1 · Safari 1
Speccss-namespaces-3
@namespace svg url("http://www.w3.org/2000/svg");

svg|a { fill: currentColor; } /* only SVG a elements, not HTML links */
Bind a prefix and use it in a type selector.

@namespace rules must come after any @charset and @import rules and before all other rules. The prefix applies only within the sheet that declares it.

@scope

Limits the rules inside it to elements within a scoping root, optionally stopping at a lower boundary called the scoping limit. Inside the block, :scope matches the root itself.

Syntax@scope <scope-boundaries>? { <block-contents> }
SupportChrome 118 · Firefox 146 · Safari 26.4
Speccss-cascade-6

<scope-boundaries> is (<scope-start>) followed optionally by to (<scope-end>), where each part is a selector list. Written with a limit it makes a donut scope: the rules apply from the root down to, but not inside, the limit.

@scope (.card) to (.card-content) {
  img { border: 2px solid; }
  :scope { padding: 1rem; }
}
A donut scope: images in a card's frame get a border, but images inside its content area do not.

When two scoped rules conflict with equal specificity, the one whose scoping root is the nearer ancestor wins. An @scope with no prelude inside a <style> element is scoped to that element's parent.