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>
...and easily set them from outside the component using style props.
<TextBox --line-color="mediumspringgreen"></TextBox>
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);
}
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;
}
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;
}
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;
}
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.
Top comments (4)
Will the css variables get available in children further down the render tree?
Yep - style props in Svelte are just syntactic sugar for setting CSS vars on a div. So
<TextBox --line-color="mediumspringgreen"></TextBox>
de-sugars intoSo the CSS vars will cascade to child elements like normal.
More details in the docs.
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?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.