Style scoping
A component's <style> is scoped to that component: its rules match the component's own markup and no further, without a shadow boundary. This is the styling half of fixing Web Components: the isolation authors actually wanted, without the cascade and theming losses that kept them away from Shadow DOM.
The scope: what a rule matches
The rules in a component's <style> match against the component's root element and its subtree, and nowhere else in the document. A bare type or class selector behaves as though written relative to the root, and every combinator works normally inside the scope: descendant (a b), child (a > b), sibling (a + b, a ~ b), and :has() all match within the subtree. The root itself is selectable as :scope.
<template component="x-card">
<defs>…</defs>
<article>
<h2>…</h2> <!-- in scope -->
<p class="lead">…</p> <!-- in scope: .lead matches here -->
<slot></slot> <!-- projected content: OUT of scope -->
<x-badge>New</x-badge> <!-- nested component: its internals OUT of scope -->
</article>
<style>
article { padding: 1rem; } /* the root */
h2 { margin: 0; } /* descendant, matches */
article > .lead { color: gray; } /* child combinator, matches */
x-badge { margin-left: auto; } /* the nested root as a box: matches */
</style>
</template>Where the scope ends
A subtree scope is not enough on its own, because a component's subtree can contain markup that is not the component's to style. Two boundaries close the scope, so it is a range with a lower limit, not just a starting point.
| Boundary | Rule |
|---|---|
| Nested components | A nested component invocation is its own scope. The enclosing component's selectors reach the nested component's root element (so it can be laid out, positioned, and sized like any child box) but must not match any element inside it. The nested component styles its own internals. |
| Projected content | Content the consumer passes through a <slot> belongs to the consumer, not the component. It keeps the consumer's scope and its own provenance (see Components), so the component's rules must not match projected nodes, even though they land inside the component's subtree. |
Without it, a component's .title { … } would silently restyle a .title inside a nested component or inside slotted content, the exact accidental-collision problem scoping exists to prevent. Closing the scope at those two boundaries is what makes "scoped to the component" mean the component's own markup, precisely.
Inheritance still crosses
Scoping constrains selector matching, not the cascade. Inherited properties (color, font, and custom properties such as design tokens) flow across every boundary above, into nested components and projected content alike, exactly as they do in ordinary light-DOM HTML. That is the point: authors get encapsulation of their own selectors while global theming, shared tokens, and the cascade keep working, which is the trade Shadow DOM refused to offer.
Vue and Svelte scoped styles, and Angular ViewEncapsulation.Emulated as the shipped default,3 won in practice over Shadow DOM because most authors want scoping (my selectors do not leak) and not isolation (nothing gets in or out). Shadow DOM, and Lit built on it, forces both.4 HTML Next makes scoping the default and treats isolation as the special case.
Isolation: opt-in, later
Full isolation via a real shadow boundary may be requested explicitly; it is never the default cost. A later Level specifies it as an extension of Declarative Shadow DOM driven by the template, closing the styling and form-participation gaps that kept authors away from Shadow DOM, rather than inheriting them.
This page defines only the behaviour. How a browser or the reference implementation produces it, native CSS <style> scoping, or attribute/hash scoping where that is unavailable, is deliberately out of scope here and documented with the reference implementation. The behaviour maps directly onto CSS @scope, whose range form (@scope (root) to (limit)) expresses exactly the root-plus-lower-limit model above.
References
- CSS Cascading and Inheritance Level 6, @scope (a style rule scoped to a subtree, with an optional lower limit forming a range).
- WHATWG HTML, Declarative Shadow DOM (the opt-in isolation path).
- Scoped-style precedent: Angular ViewEncapsulation.Emulated, its shipped default, is attribute-hash scoping with no shadow boundary, the exact mechanism described here; Vue
<style scoped>and Svelte scoped styles are the same scoping-not-isolation approach. - The isolation contrast authors avoided: Shadow DOM and Lit component styles impose a real shadow boundary that also blocks shared theming and the cascade.