Code makes apps work, but styling makes them delightful. Imagine a to-do app that functions perfectly — but everything is plain Times New Roman, unstyled checkboxes, and buttons straight out of 1995. It works… but no one wants to use it.
That’s where styling comes in. A clean, well-designed UI makes your app feel modern, usable, and trustworthy.
Svelte doesn’t just give you reactive components — it also gives you a built-in styling model. Unlike React (where you often reach for extras like CSS Modules or styled-components), Svelte treats styles as a first-class citizen, right inside your .svelte files.
👉 By the end of this guide, you’ll be able to:
Write scoped CSS that applies only to one component.
Escape the scope with :global when needed.
Use class directives for dynamic, reactive styling.
Supercharge your workflow with Tailwind CSS utilities.
Let’s begin with the foundation: scoped styles — Svelte’s secret to avoiding CSS spaghetti.
Step 1: Scoped CSS — The Svelte Way
Every Svelte component (.svelte file) can contain a block. The key difference from plain HTML/CSS:
In regular HTML, a tag applies globally across the whole page.
In Svelte, a tag inside a component is scoped: it only affects the markup in that same file.
This is what makes component-based styling safe and predictable.
Example: A Styled Button
src/lib/Button.svelte
// This is a reusable button component.// "label" is a prop passed in by the parent.exportletlabel="Click me";{label}button{background:royalblue;color:white;border:none;padding:0.5rem1rem;border-radius:6px;cursor:pointer;}
✅ The components have the royal blue styling. ✅ The plain at the bottom looks unstyled — proving styles don’t leak out. ✅ You don’t need unique class names like .button-primary or .button-danger to avoid conflicts — scoping is automatic.
Step 2: How Scope Actually Works
So how does Svelte keep your styles from leaking out?
Behind the scenes, the compiler rewrites your CSS selectors to be unique per component.
Example: Your Code
If you write this in Button.svelte:
button{background:royalblue;}
What Svelte Compiles To
Svelte generates a random hash (like svelte-xyz123) and rewrites your code:
button.svelte-xyz123{background:royalblue;}
And the element in the DOM gets:
class="svelte-xyz123">Save
✅ Result: the style applies only to buttons in this component. ❌ Other s elsewhere in your app aren’t affected.
You don’t need to worry about those svelte-xyz123 classes — Svelte manages them automatically. But knowing this explains why scoped CSS “just works.”
👉 This is why Svelte projects don’t descend into CSS spaghetti, where one stylesheet accidentally overrides another.
Step 3: Escaping Scope with :global
By default, styles inside a component are scoped — they only apply locally. But sometimes, you really do need styles that affect the entire app:
A CSS reset (removing browser defaults).
Global typography (setting fonts and line-heights).
Now every page in your app inherits these global styles.
Example: Mixing Scoped and Global
You can also combine scoped selectors with global ones:
/*Only<span>insidethiscomponent’s<button>arestyled*/button:global(span){color:yellow;}
Normal highlighted text
Here, the remains scoped, but the span inside it is styled using a global selector.
⚠️ Rule of thumb: Stay scoped by default (safer, predictable). Use :global only for resets, shared typography, or app-wide theming.
Step 4: Dynamic Classes with class: Directive
Static styles are nice, but real UIs are dynamic. Think about:
A “selected” button in a toolbar.
An “active” tab in navigation.
A form field that turns red when invalid.
You don’t want to hard-code these classes — you want them to respond to state.
Example: Toggling an Active Class
src/lib/ToggleButton.svelte
letactive=false;// local stateclass:active={active}on:click={()=>active=!active}>{active?"Active":"Inactive"}/* Normal style */button{padding:0.5rem1rem;border:none;cursor:pointer;}/* Extra style when active */.active{background:green;color:white;}
✅ Now when you click the button, the active class toggles on and off automatically. ❌ Without class:, you’d have to write something messy like:
class={active?"active":""}>...
Svelte’s class: directive keeps it clean, reactive, and declarative.
Step 5: Multiple Class Directives
Sometimes a component can have different visual variants. Think of buttons: primary, danger, success, outline… you don’t want to make a new component for each.
With Svelte, you can stack multiple class: directives to toggle different classes based on state or props.
✅ The button automatically switches between green blue and red depending on the type. ✅ You can add as many variants as you want (success, warning, etc). ❌ Without class directives, you’d need to manually concatenate classes, which quickly gets messy.
Step 6: Inline Styles
Most of the time you’ll use CSS classes for styling, but sometimes you need a quick dynamic style — like controlling the exact width, height, or color of an element from a variable.
Svelte lets you bind styles directly inside the style attribute.
Example: Dynamic Box
src/lib/DynamicBox.svelte
<script>// Reactive variable that controls sizeletsize=$state(50);
✅ The box grows every time you click the button. ✅ Inline styles are fully reactive — any change to size updates the DOM instantly.
⚠️ Best practice: Use inline styles only for one-off, highly dynamic values (like size, position, or transform). For reusable styles (colors, padding, hover states), stick to CSS classes, which are easier to maintain and theme.
Quick Recap
So far, you’ve learned:
Svelte styles are scoped by default.
:global lets you escape scope for resets or shared rules.
class: directives toggle classes reactively.
Inline styles work for dynamic values.
You’ve now mastered the foundation of Svelte styling: scoped CSS, global overrides, class toggling, and inline styles. With these tools, you can already build clean, reusable components that look and behave consistently.
👉 In the next article, we’ll take things further with motion and theming — transitions, animations, and strategies for handling light/dark modes.