Styles That Stay in Their Lane
In traditional CSS, styles are global. A .card class in one file affects .card elements everywhere. Svelte solves this with automatic scoping — styles in a component only affect that component.
What You’ll Learn
- How Svelte scopes styles automatically
- Why scoped styles prevent conflicts
- When styles do and don’t apply
- How to inspect scoped styles
The Global CSS Problem
Traditional CSS creates conflicts:
/* styles.css */
.button {
background: blue;
}
/* later in another file... */
.button {
background: red; /* Overwrites the first! */
} Every .button is now red, even ones you didn’t mean to change.
Solutions like BEM naming conventions help:
.header__button--primary { }
.card__button--secondary { } But they’re verbose and rely on developer discipline.
Svelte’s Approach
In Svelte, styles in a component’s <style> block are scoped automatically:
<!-- filename: src/lib/components/ServiceCard.svelte -->
<style>
.card {
border: 1px solid #ddd;
}
h3 {
color: blue;
}
</style>
<article class="card">
<h3>Lawn Mowing</h3>
</article> These styles only affect elements inside this component. Other components with .card or h3 elements are unaffected.
How It Works
Svelte adds unique class attributes to elements:
<!-- What you write -->
<article class="card">
<h3>Lawn Mowing</h3>
</article>
<!-- What Svelte generates -->
<article class="card svelte-abc123">
<h3 class="svelte-abc123">Lawn Mowing</h3>
</article> And transforms your CSS:
/* What you write */
.card { border: 1px solid #ddd; }
h3 { color: blue; }
/* What Svelte generates */
.card.svelte-abc123 { border: 1px solid #ddd; }
h3.svelte-abc123 { color: blue; } The unique class (svelte-abc123) ensures styles only match elements in this component.
See It in Action
Create two components with the same class names:
<!-- filename: src/lib/components/ComponentA.svelte -->
<style>
.box {
background: lightblue;
padding: 1rem;
}
</style>
<div class="box">Component A</div> <!-- filename: src/lib/components/ComponentB.svelte -->
<style>
.box {
background: lightgreen;
padding: 1rem;
}
</style>
<div class="box">Component B</div> Use both:
<script>
import ComponentA from '$lib/components/ComponentA.svelte';
import ComponentB from '$lib/components/ComponentB.svelte';
</script>
<ComponentA />
<ComponentB /> Result: Component A is blue, Component B is green. No conflict!
Inspect in DevTools
Open your browser’s DevTools and inspect an element:
<div class="box svelte-xyz789">Component A</div> The svelte-xyz789 class is added automatically. The CSS selector is:
.box.svelte-xyz789 {
background: lightblue;
padding: 1rem;
} What Gets Scoped
Svelte scopes:
Class selectors:
.card { } /* → .card.svelte-xxx */
.btn.primary { } /* → .btn.primary.svelte-xxx */ Element selectors:
h3 { } /* → h3.svelte-xxx */
button { } /* → button.svelte-xxx */ ID selectors:
#main { } /* → #main.svelte-xxx */ Attribute selectors:
[type="text"] { } /* → [type="text"].svelte-xxx */ Pseudo-classes and pseudo-elements:
.btn:hover { } /* → .btn.svelte-xxx:hover */
h3::before { } /* → h3.svelte-xxx::before */ What Doesn’t Get Scoped
Styles for child components are NOT applied:
<!-- filename: Parent.svelte -->
<script>
import ServiceCard from './ServiceCard.svelte';
</script>
<style>
/* ❌ This won't work! */
.service-card h3 {
color: red;
}
</style>
<ServiceCard /> Why? The h3 inside ServiceCard has a different scoping class than the parent. The selector .service-card.svelte-parent h3.svelte-child doesn’t match.
To style child components, you need:
- Global styles (covered later)
- CSS custom properties (covered later)
- The
:global()modifier (covered later)
Benefits of Scoping
No naming conflicts — Use simple class names like .card, .button, .title without worry.
Refactoring is safe — Change styles in one component without side effects.
Delete with confidence — Remove a component and its styles go too.
Smaller mental model — Focus on one component at a time.
Unused Style Warnings
Svelte warns about unused selectors:
<style>
.card { }
.unused-class { } /* Warning: unused CSS selector */
</style>
<div class="card">Hello</div>
<!-- .unused-class is never used --> This helps keep your CSS clean and free of dead code.
Common Mistakes
Expecting Parent Styles to Apply to Children
<!-- Parent.svelte -->
<style>
/* ❌ Won't affect Child's elements */
p { color: red; }
</style>
<Child /> <!-- Child's <p> won't be red --> Thinking IDs Work Differently
<style>
/* IDs are scoped too! */
#header { } /* → #header.svelte-xxx */
</style> Dynamic Classes Require the Class in Styles
<style>
/* Must exist for the class to work */
.active { }
</style>
<button class={isActive ? 'active' : ''}>
Click
</button> Summary
Svelte automatically scopes styles to the component where they’re defined. It adds unique class attributes to elements and modifies selectors to match. This prevents conflicts and makes CSS maintenance easier.
Key takeaways:
- Styles in
<style>are automatically scoped - Svelte adds unique class attributes to achieve this
- Parent component styles don’t affect child component internals
- No more naming conflicts or accidental overrides
Next Steps
Now that you understand scoping, let’s apply it. Continue with Style the ServiceCard to make the service display look professional.