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.