DEV Community

Geoff Rich
Geoff Rich

Posted on • Originally published at geoffrich.net

Svelte quick tip: Style prop defaults

Svelte has a built-in solution for component theming using style props. So, you can use CSS custom properties in your component styles...

<p>
    I have something important to say.
</p>

<style>
    p {
        border: 2px solid var(--line-color);

        /* Decorative styles */
        padding: 1rem;
        max-width: 60ch;
    }

</style>
Enter fullscreen mode Exit fullscreen mode

...and easily set them from outside the component using style props.

<TextBox --line-color="mediumspringgreen"></TextBox>
Enter fullscreen mode Exit fullscreen mode

But what if you want your style to have a default value? The custom property var syntax takes a second argument that sets a fallback if the property is not defined. So, if you wanted the default border color to be darkred, you could do the following.

p {
  border: 2px solid var(--line-color, darkred);
}
Enter fullscreen mode Exit fullscreen mode

However, this can get verbose if you want to use --line-color in multiple places, with the same fallback. If you want to update the default value, you have to do it multiple places!

p {
  border: 2px solid var(--line-color, darkred);
  text-decoration: underline wavy var(--line-color, darkred) 1px;
}
Enter fullscreen mode Exit fullscreen mode

There’s two ways to refactor this to make it less verbose. First, you could introduce another custom property for the default value:

p {
  --line-color-default: darkred;
  border: 2px solid var(--line-color, var(--line-color-default));
  text-decoration: underline wavy var(--line-color, var(--line-color-default)) 1px;
}
Enter fullscreen mode Exit fullscreen mode

This makes it so there’s one place to change the default value, but you still have to provide the second argument to var every time you reference --line-color.

Instead, my recommended approach would be to introduce another custom property that represents either line-color or the fallback.

p {
  --_line-color: var(--line-color, darkred);

  border: 2px solid var(--_line-color);
  text-decoration: underline wavy var(--_line-color) 1px;
}
Enter fullscreen mode Exit fullscreen mode

So now you have two variables:

  • --line-color is the user-supplied theme value
  • --_line-color is what we use in our styles, and will either be the user-supplied value (if defined) or the default color

You can see this in action in this Svelte REPL.

You only need to introduce a variable like --_line-color if you plan on using the theme variable multiple places. Otherwise, it’s perfectly fine to set the fallback where you use the property, as in the first example.

The technique on display here is not unique to Svelte, and can be applied anywhere you use custom properties. However, it's of particular interest with Svelte, since custom properties are the recommended way of theming a component.

Discussion (4)

Collapse
paratron profile image
Christian Engel

Will the css variables get available in children further down the render tree?

Collapse
geoffrich profile image
Geoff Rich Author

Yep - style props in Svelte are just syntactic sugar for setting CSS vars on a div. So <TextBox --line-color="mediumspringgreen"></TextBox> de-sugars into

<div style="display: contents; --line-color: "mediumspringgreen">
  <TextBox></TextBox>
</div>
Enter fullscreen mode Exit fullscreen mode

So the CSS vars will cascade to child elements like normal.

More details in the docs.

Collapse
paratron profile image
Christian Engel

Thanks for the clarification!

Do IDEs assist with that? For regular props its fairly easy since they are announced with export let foo; but how does it work for style props? Or do I need to assign them blindly?

Thread Thread
geoffrich profile image
Geoff Rich Author

For now, there's no way for component authors to specify which style props are available, aside from documentation. There's an open issue on the Svelte language tools (which makes IDE autocomplete work) to implement this feature.