profile
viewpoint

Ask questionsJavascript Interpolation in Styles via CSS Variables?

Back in November, there was some discussion of whether svelte could support javascript variable interpolation in style sheets (sveltejs/svelte.technology#9). Unfortunately, this didn't seem feasible, since CSS classes are per class, and can't be specialized per instance.

It seems like this could be possible using CSS variables. Consider adding the following (functional) svelte code to the standard counter example:

<div class="bar" style="--count:{{count}}"></div>

<style>
  .bar {
    width: calc(40px * var(--count) );
  }
</style>

This sort of pattern might be attractive for a a few reasons:

  • It avoids messy inline styling.
  • It allows pure CSS for certain kinds of interactivity (eg. :hover).
  • It may (?) be faster in certain situations like spritemap manipulation where one wants to prevent certain parts of the style from being updated? (I don't understand the underlying model well enough to know if or when this would be true).

(To be clear, I've only done a few toy things this way. I'm completely open to the possibility that it may not be a good idea at all!)

While the example above works, and might be handy, it seems unnecessarily clunky. It also runs a risk that CSS variables may end up with different names than their JS counterparts, accumulating technical debt.

Potential Svelte Integration

If svelte wanted to facilitate this kind of pattern, there's a broad spectrum of options it could take.

  1. At the very mild end, it could support a kind of CSS variable binding syntax. This could cut down on repretition and encourage variable names to stay in sync:
<div class="bar" css_bind:count ></div>
  1. The next step up might be to detect variable usage and implicitly bind them. So you'd just do this without explicitly setting up --count:
width: calc(40px * var(--count) );
  1. Yet stronger might be to allow javascript variables through the usual syntax and implicitly create the css variable:
width: calc(40px * {{count}} );

Or maybe this, if you wanted to still remind the user that it's a variable:

width: calc(40px * var({{count}}) );
  1. Finally, one could go all the way in:
width:  {{40 * count}}px;

Which would compile into something like:

<div class="bar" style="--svelte-css-1234:{{40 * count}}px"></div>

<style>
  .bar {
    width: var(--svelte-css-1234);
  }
</style>

Potential Downsides

  • This could lead to less transparent CSS code when inspected in the browser. Compare:
width: 120px;

--count: 3;
width: calc(40px * var(--count) );

--svelte-css-1234: 120px;
width: var(--svelte-css-1234);

On the flip side, it may make some things more transparent -- especially if we can keep variable names synced to javascript.

  • It may also complicate the model for understanding what your code is doing behind the scenes...
sveltejs/svelte

Answer questions saabi

Here's a REPL with the Counter example in v3.

It's not too cumbersome, but some support for doing it directly in CSS, while keeping compatibility with other preprocessors would be great.

Something like this:

<input type=number bind:value={count}>
<button on:click={() => count += 1}>count</button>
<div class="count-bar"></div>

<!--
var-prefix could be optional, default would just be '--' which 
would replace all CSS vars that match a component variable.
However, specifying it improves readability as it makes 
intent explicit.
And perhaps it should be required to enable replacement, so
as to avoid surprises with unexpected replaced vars.
-->
<style var-prefix='--s-'> 
	.count-bar {
		height: 40px;
		width: calc( var(--s-count) * 1px + 1px);
		background-color: gray;
	}
</style>
useful!

Related questions

Svelte applications fail to load in IE 11. Similar loading issue with the main site svelte.dev hot 1
Uncaught TypeError: y.fragment.l is not a function hot 1
Increase elements when using a combination of await block and transition hot 1
Svelte applications fail to load in IE 11. Similar loading issue with the main site svelte.dev hot 1
Change body class via &lt;svelte:body /&gt; hot 1
Site: Svelte Cookbook/ Examples hot 1
Missing dependencies in svelte/compiler type definitions hot 1
Multiple components in one file - svelte hot 1
onMount Error: Function called outside component initialization hot 1
A way to see if slot prop is present hot 1
slot inside #if inside #each : TypeError cannot read property 'd' of null hot 1
unclear / difficult to import uuidv4 hot 1
Use <script type="application/ld+json"> in <svelte:head> hot 1
Error: 'default' is not exported when using babel + moment hot 1
Outros undefined when transitioning to another page hot 1
Github User Rank List