How I Style My Svelte Components

December 14, 2021 by Steven Ng

Background

So this is just a not-so-little post about how I do styling (as a process) in Svelte, and spoiler alert, I don't use Tailwind.

I understand the appeal of Tailwind, but I there's a herd mindset around it. Like most technologies, whether it be web frameworks or whatnot, people who mentally invest in them can get quite... religious about them. As a result, any attempt at discourse tends to be one-sided.

And of course, you can use Tailwind with Svelte. In fact, a lot of people do, and are very happy doing so. And that's great.

But unless a project dictates that I use it, I generally don't find a need for it.

On Premade Component Libraries

As a rule, I generally don't use a premade component library, like IBM's Carbon, Bootstrap or Material UI. Years ago, when I built a web based project management solution, I used Material UI for AngularJS, and had... mixed results.

The problem (for me) with these canned components is that they get you from point A to B fairly quickly, but unless your end goal is a cookie cutter user interface, you'll end up rewriting or modifying components to make things work the way you want to.

When you're trying to build something that is in your mind, special, you often need to make your interface components do more than what canned libraries offer. You then end up forking the code (because nobody wants to be at the mercy of another party's development or fix schedule), and making your own UI components. I'm obviously not adverse to using canned libraries if a project dictates that I do. If, however, my final objective is a better mousetrap, I rarely go in that direction.

Clearly I'm not the only one thinking that, because Tailwind is a great, flexible alternative to canned component libraries (notably Tailwind UI). It has clearly struck a chord with a lot of front end developers. It's probably a good option for users of a lot of web frameworks too. If I was still using AngularJS, I'd probably use Tailwind. But because I use Svelte, that ship has sailed.

My main argument, however, is that I don't consider Tailwind to be a must-use tool for all Svelte developers.

Svelte Styling

One of the reasons why I like Svelte is that styling is tightly scoped within components. Any CSS you create for a component is specific to that component unless you scope your CSS differently (using :global). Depending on the number of HTML elements in your Svelte component, you may not even need to use any classes. At all.

Take this obviously fake and oversimplified component:

<!-- TextInput.svelte -->
<script>
  export let name;
  export let label;
</script>
<div>
  <label>{label}</label>
  <input type="text" name="name" bind:value={name}/>
</div>

In Svelte, you don't need classes to style the component. Svelte already applies a unique class (at compile time) to the component to ensure that CSS that you write for the component is isolated to that component. So for CSS, you can simply apply the styles to the elements themselves:

<style>
  div {
    padding: 20px;
  }
  label {
    font-weight: bold;
  }
  input {
    border-color: blue;
  }
</style>

Unless you have multiples of elements, you don't even need any classes at all. Even if you do have multiples, your option isn't limited to using classes. If it's appropriate, you can create child components with their own styling. You can also use attributes in your HTML elements. For example <div type="sometype"> and then style for [type="sometype"]. In any case, all the styles in your .svelte file's <style> block are still isolated to the component.

If you have your own CSS library (like I do), you already have common rules about styling for most common components. There's no need to be cluttering your HTML markup with classes when most of the time the default styles in your .svelte file are exactly what you need.

If you need to theme or customize your look and feel, you have options. One of them is obviously Tailwind. Another is using CSS variables, like I do.

While people tend to firmly put themselves into one camp or another, I don't take the approach that there is one true way to do things. There is always more than one way to do something, and there may be good reasons for not doing it the way the herd is doing it.

Global Level Styling

For styling Svelte, I basically have a small CSS reset file and a library of CSS variables (aka. CSS custom properties). CSS custom properties basically work on all modern browsers, so unless you're required to support IE 11, which has been EOL'ed, it's a better solution (in my opinion) than adding a pile of classes into every HTML attribute.

There are obviously some preconditions that need to be met in order for this system to work, and the most important one is that all your components are under your control. If you like to use components from other libraries "as is", then this strategy isn't great. As mentioned earlier, I personally don't like to use components from other libraries "as is", because those components tend to be designed to be everything to everyone, which often doesn't work out when your application has very specific requirements and UI behaviour expectations (which is more often than not).

CSS Resets

The purpose of a reset file is to make all browsers use the same defaults. If you're unfamiliar with resets, see here and here for more information.

The job of the reset mostly comes in the form of setting margins, padding and line heights. This way, when you apply your CSS styles, your pages will render with a consistent look across all browsers and operating systems.

My reset file isn't very large, and I basically use it in all my projects as my CSS starting point.

CSS Variables

I guess the easiest way to describe how I style my web apps now is that it is driven by CSS variables. The approach is very similar to using Pollen. I hadn't heard of Pollen when I came up with my approach, but outside of variable naming conventions, it's virtually the same as what I do.

If you're unfamiliar with CSS variables, you would declare the like this:

:root {
  --lightest-gray: #dee2e6;
  --lighter-gray: #ced4da;
  --light-gray: #adb5bd;
  --gray: #868e96;
  --dark-gray: #495057;
  --darker-gray: #343a40;
  --darkest-gray: #212529;
  --black: var(--darkest-gray);
  --white: #ffffff;
}

And to use the variable, you would do something like this:

.some-selector {
  background-color: var(--light-gray)
}

You can also assign variables to other variables:

:root (
  --light-gray: #eeeeee;
  --component-border: var(--light-gray)
}

Perhaps the handiest thing about CSS custom properties is that you can assign a fallback. This lets you set sensible defaults in the absence of a CSS variable:

background-color: var(--light-gray, #efefef);

If your browser can't find the --light-gray variable, the element's background will use #efefef as the default.

So in a Svelte component...

<script>
  // ...your code here
</script>

<div>
  I'm a custom component!
</div>

<style>
  div {
    background-color: var(--light-gray, #EFEFEF)
    border-color: var(--component-border, #000000)
  }
</style>

And in use:

<!-- the tailwind way -->
<MyCustomComponent class="bg-gray-100 border-black"/>

<!-- look ma, no classes! -->
<MyCustomComponent/>

Why This Approach Works For Me

So one gotcha with my approach is that if you don't have any existing styling CSS from past work, this approach is potentially a lot of effort to get into a workable solution.

Over the years, I've amassed my own building blocks for web based UI components that I have either built or forked from other libraries. It's an opinionated set of components that I have been reusing as the foundation of my web projects for quite some time. Because of this, all my apps share a very similar look and feel.

It did not take long to convert everything into CSS variables, nor did it take a long time to get the variables integrated into my Svelte code.

You may be unlucky and not have a foundation of CSS styling to start with. If, however, this approach does seem like your preferred way of working, I would suggest using Pollen as your starting point, as it has all the fundamentals covered.

Relax Tailwinder, Keep On Tailwinding

Finally, if comments about Tailwind at Hacker News or Reddit are any indicator, Some Tailwind enthusiasts will probably see red-900 and hate this post. ¯\_(ツ)_/¯

Instead of reacting, maybe take a breath. This post is just about my approach to styling Svelte. While I am saying Tailwind isn't for me, I did not say that it isn't for you.

Now get back to Tailwinding like a boss, and forget that you ever saw this post. You'll be much better for it.