Scoped Styles
A component's <style> block only reaches that component's
own elements. Write .btn in one file and it cannot touch
.btn in another.
No BEM, no CSS modules, no prefixes.
<!-- SaveButton.html -->
<button class="btn">Save</button>
<style>
.btn {
background: var(--accent);
color: var(--bg-primary);
border: 0;
border-radius: 6px;
padding: 0.45rem 1rem;
font: inherit;
font-weight: 600;
cursor: pointer;
}
</style>Another component's .btn is simply a different .btn:
<!-- CancelButton.html -->
<button class="btn">Cancel</button>
<style>
.btn {
background: none;
color: var(--text-secondary);
border: 1px solid var(--border-hover);
border-radius: 6px;
padding: 0.45rem 1rem;
font: inherit;
cursor: pointer;
}
</style>Put both on a page and neither one wins:
How it works
Each component gets a hash class of its own, like jaf-x7k2m.
The class is added to every element in the component's markup, and to every part of every selector in its styles:
/* What you write */
.card { border: 1px solid gray; }
.card .title { font-size: 1.5rem; }
.btn:hover { background: navy; }
/* What the browser gets */
.card.jaf-x7k2m { border: 1px solid gray; }
.card.jaf-x7k2m .title.jaf-x7k2m { font-size: 1.5rem; }
.btn.jaf-x7k2m:hover { background: navy; }
That is the whole mechanism.
Pseudo-classes, pseudo-elements, media queries and nesting all behave normally, because nothing else about your CSS changes.
Plain tag selectors are safe too
p becomes p.jaf-x7k2m, so styling a bare tag is a
local decision rather than a site-wide one:
<p>Only this component's paragraphs turn grey.</p>
<style>
p {
margin: 0;
color: var(--text-tertiary);
font-style: italic;
}
</style>Only this component's paragraphs turn grey.
The same goes for *: a component's
* { margin: 0 } becomes *.jaf-x7k2m and resets its
own elements, not the document's.
What scoping does not reach
Two selectors are left alone, because they address elements outside the
component: anything starting with :root, and anything starting
with :host.
Two kinds of element never get the hash class, so scoped rules cannot style them:
- Slot content - the markup came from whoever used your component, and carries their hash.
-
Child components - a
<Card />tag in your markup keeps its own scope. Style it from insideCard, or pass it a prop.
<style global>
is the deliberate escape, and it is what you want for slot content, resets
and themes. See Global Styles.