There’s been a lot of well-deserved hype around Svelte recently, with the project accumulating over 24,000 GitHub stars. Arguably the simplest JavaScript framework out there, Svelte was written by Rich Harris, the developer behind Rollup. There’s a lot to like about Svelte (performance, built-in state management, writing proper markup rather than JSX), but the big draw for me has been its approach to CSS.
Single file components
React does not have an opinion about how styles are defined
—React Documentation
A UI framework that doesn’t have a built-in way to add styles to your components is unfinished.
—Rich Harris, creator of Svelte
In Svelte, you can write CSS in a stylesheet like you normally would on a typical project. You can also use CSS-in-JS solutions, like styled-components and Emotion, if you’d like. It’s become increasingly common to divide code into components, rather than by file type. React, for example, allows for the collocation of a components markup and JavaScript. In Svelte, this is taken one logical step further: the Javascript, markup and styling for a component can all exist together in a single `.svelte` file. If you’ve ever used single file components in Vue, then Svelte will look familiar.
// button.svelte
<style>
button {
border-radius: 0;
background-color: aqua;
}
</style>
<button>
<slot/>
</button>
Styles are scoped by default
By default, styles defined within a Svelte file are scoped. Like CSS-in-JS libraries or CSS Modules, Svelte generates unique class names when it compiles to make sure the styles for one element never conflict with styles from another.
That means you can use simple element selectors like div
and button
in a Svelte component file without needing to work with class names. If we go back to the button styles in our earlier example, we know that a ruleset for <button>
will only be applied to our <Button>
component — not to any other HTML button elements within the page. If you were to have multiple buttons within a component and wanted to style them differently, you’d still need classes. Classes will also be scoped by Svelte.
The classes that Svelte generates look like gibberish because they are based on a hash of the component styles (e.g. svelte-433xyz
). This is far easier than a naming convention like BEM. Admittedly though, the experience of looking at styles in DevTools is slightly worse as the class names lack meaning.

It’s not an either/or situation. You can use Svelte’s scoped styling along with a regular stylesheet. I personally write component specific styles within .svelte
files, but make use of utility classes defined in a stylesheet. For global styles to be available across an entire app — CSS custom properties, reusable CSS animations, utility classes, any ‘reset’ styles, or a CSS framework like Bootstrap — I suggest putting them in a stylesheet linked in the head of your HTML document.
It lets us create global styles
As we’ve just seen, you can use a regular stylesheet to define global styles. Should you need to define any global styles from within a Svelte component, you can do that too by using :global
. This is essentially a way to opt out of scoping when and where you need to.
For example, a modal component may want to toggle a class to style the body element:
<style>
:global(.noscroll) {
overflow: hidden;
}
</style>
Unused styles are flagged
Another benefit of Svelte is that it will alert you about any unused styles during compilation. In other words, it searches for places where styles are defined but never used in the markup.
Conditional classes are terse and effortless
If the JavaScript variable name and the class name is the same, the syntax is incredibly terse. In this example, I’m creating modifier props for a full-width button and a ghost button.
<script>
export let big = false;
export let ghost = false;
</script>
<style>
.big {
font-size: 20px;
display: block;
width: 100%;
}
.ghost {
background-color: transparent;
border: solid currentColor 2px;
}
</style>
<button class:big class:ghost>
<slot/>
</button>
A class of ghost
will be applied to the element when a ghost
prop is used, and a class of big
is applied when a big
prop is used.
<script>
import Button from './Button.svelte';
</script>
<Button big ghost>Click Me</Button>
Svelte doesn’t require class names and prop names to be identical.
<script>
export let primary = false;
export let secondary = false;
</script>
<button
class:c-btn--primary={primary}
class:c-btn--secondary={secondary}
class="c-btn">
<slot></slot>
</button>
The above button component will always have a c-btn
class but will include modifier classes only when the relevant prop is passed in, like this:
<Button primary>Click Me</Button>
That will generate this markup:
<button class="c-btn c-btn--primary">Click Me</button>
Any number of arbitrary classes can be passed to a component with a single prop:
<script>
let class_name = '';
export { class_name as class };
</script>
<button class="c-btn {class_name}">
<slot />
</button>
Then, classes can be used much the same way you would with HTML markup:
<Button class="mt40">Click Me</Button>
From BEM to Svelte
Let’s see how much easier Svelte makes writing styles compared to a standard CSS naming convention. Here’s a simple component coded up using BEM.
.c-card {
border-radius: 3px;
border: solid 2px;
}
.c-card__title {
text-transform: uppercase;
}
.c-card__text {
color: gray;
}
.c-card--featured {
border-color: gold;
}
Using BEM, classes get long and ugly. In Svelte, things are a lot simpler.
<style>
div {
border-radius: 3px;
border: solid 2px;
}
h2 {
text-transform: uppercase;
}
p {
color: gray;
}
.featured {
border-color: gold;
}
</style>
<div class:featured>
<h2>{title}</h2>
<p>
<slot />
</p>
</div>
It plays well with preprocessors
CSS preprocessors feels a lot less necessary when working with Svelte, but they can work perfectly alongside one another by making use of a package called Svelte Preprocess. Support is available for Less, Stylus and PostCSS, but here we’ll look at Sass. The first thing we need to do is to install some dependencies:
npm install -D svelte-preprocess node-sass
Then we need to import autoPreprocess in rollup.config.js
at the top of the file.
import autoPreprocess from 'svelte-preprocess';
Next, let’s find the plugins array and add preprocess: autoPreprocess()
to Svelte:
export default {
plugins: [
svelte({
preprocess: autoPreprocess(),
...other stuff
Then all we need to do is specify that we’re using Sass when we’re working in a component file, using type="text/scss"
or lang="scss"
to the style tag.
<style type="text/scss">
$pink: rgb(200, 0, 220);
p {
color: black;
span {
color: $pink;
}
}
</style>
Dynamic values without a runtime
We’ve seen that Svelte comes with most of the benefits of CSS-in-JS out-of-the-box — but without external dependencies! However, there’s one thing that third-party libraries can do that Svelte simply can’t: use JavaScript variables in CSS.
The following code is not valid and will not work:
<script>
export let cols = 4;
</script>
<style>
ul {
display: grid;
width: 100%;
grid-column-gap: 16px;
grid-row-gap: 16px;
grid-template-columns: repeat({cols}, 1fr);
}
</style>
<ul>
<slot />
</ul>
We can, however, achieve similar functionality by using CSS variables.
<script>
export let cols = 4;
</script>
<style>
ul {
display: grid;
width: 100%;
grid-column-gap: 16px;
grid-row-gap: 16px;
grid-template-columns: repeat(var(--columns), 1fr);
}
</style>
<ul style="--columns:{cols}">
<slot />
</ul>
I’ve written CSS in all kinds of different ways over the years: Sass, Shadow DOM, CSS-in-JS, BEM, atomic CSS and PostCSS. Svelte offers the most intuitive, approachable and user-friendly styling API. If you want to read more about this topic then check out the aptly titled The Zen of Just Writing CSS by Rich Harris.
Happy to see a Svelte article on CSS Tricks! Your BEM to Svelte explanation says it all. Why have we been writing BEM for all these years when a compiler could do it for us?
Although maybe should not promote writing code where we style base elements directly. Even in scoped code this could easily lead to problems when expanding upon the code. A better idea in my opinion is to use simple class names like in Bootstrap. In Svelte, you don’t have the CSS clashes that Bootstrap has due to its lack of namespacing.
Been playing with Svelte CSS and context based theme using CSS Properties (CSS Vars). This is incredibly powerful. Should be much more performant than approach used normally used with Styled Components and Emotion. Specially with new CSS.registerProperty() which allows for CSS vars to not inherit making them more performant as they will only affect one element.
https://codesandbox.io/s/svelte-theme-components-crt54?fontsize=14&module=%2FButton.svelte
Nice post! Have you used external sass-files? I’m having a pain in the rear trying to import a single global.scss file, even though preprocessing sass in the style tags is working.
Cheers!
In
App.svelte
:That’s what I’m using today, but then I can’t use the sweet component styling for App :P It’s not that bad, but it would be nice with a better solution.
So, you can’t separate style from component? Because the first part of this article kinda implies you can. I don’t like single file components. I like to reuse my CSS and I like SASS a lot. And making a huge pile of everything but the kitchen sink is definitely not a “taking one step further”. Unless it’s a step into the abyss.
You can separate things.
I don’t think “Unused styles are flagged”
I think it they are actually removed by default https://github.com/sveltejs/svelte/issues/697 but than not sure how one can actually instruct Svelte to not remove specific styles ?
I find it funny that this is what you liked about Svelte because this was actually what made me finally give up on Svelte (that and debugging with it was a nightmare).
Yes, everything you mentioned is great – but you’ll notice almost all of it has to do with either styling from within your component or extra boilerplate and verbosity to allow you to style things from outside the component. While you usually need some internal CSS, only styling from within your component is NOT how the real world works. What’s the first thing you do when you start a new web project? Throw a reset.css file in there.
I want a headless UI component system, and that was a nightmare with Svelte for me. They purposefully made it very hard to actually style your components in the normal, natural way of applying styles to the component tag itself without a ton of boilerplate. For example:
The class solution you presented to pass a class into the component was not dependable for me. Svelte started randomly overwriting classes I passed in when it assigned its scoping class behind the scenes. I could not find a way to add a class to a component from outside the component that was dependable in a production environment. Not being able to add a class to an element is incredibly limiting.
You can use :global to style elements, but it’s truly global, meaning it leaks outside of your file to everything on your page – not just the elements and components in the file you’re working in. So you have to wrap everything in a div and scope it to that div everywhere in your code. Additionally, when you use a lot of components, your style sheet just looks like one big long list of :globals() and is difficult to visually parse.
Perhaps worst of all, in Svelte, the component tag you use does not actually represent a real element in the document, similar to a template tag in html. That makes sense with a template tag because you can easily target the elements inside, but not with an enclosed component where you ALSO can’t easily target the things inside. The two ideas are diametrically opposed in my mind.
This caused all sorts of problems and unnatural ways I had to do things:
I couldn’t target the tag name of an element in my CSS – in the same document the tag was in (without wrapping it in yet another :global selector).
I couldn’t do something like put an id on the component and target items in the component’s slot with it. I would have to either wrap another needless div around the component and scope the slotted items with that, or I would have to a.) add a custom property in the component called id, b.) export it, c.) assign an id to my component, d.) in my css, even though I’m targeting the children inside a tag with an id assigned all in this document, I would still have to wrap it in a :global selector.
I couldn’t even position the component itself on my page without adding extra boilerplate inside the component or wrapping it in yet another extra needless div – and in both cases still having to use another :global selector.
The result is, I ended up finding myself wrapping all my components in extra divs, which was just ugly and messy and bloated my html. And they’re aware of this too because not only do they recommend you do this as the solution, but they actually do this behind the scenes themselves with syntactic sugar features such as css variable properties. This can create even more problems because they’re inserting invisible divs into your html that you actually CAN target with your CSS.
I just found accessing anything inside a component to style it or programmatically alter it was just a mess of extra boilerplate I had to set up. I finally realized I was increasing time by using Svelte instead of saving it.
Having said that, I’m sure I did not learn every trick or approach to doing this and there might be better ways, but for me, Svelte provided the opposite of an intuitive, approachable and user-friendly styling API.