sveltejs / svelte

Cybernetically enhanced web apps
https://svelte.dev
MIT License
78.93k stars 4.15k forks source link

Javascript Interpolation in Styles via CSS Variables? #758

Closed colah closed 4 years ago

colah commented 7 years ago

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:

(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

--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...
Conduitry commented 7 years ago

I do really like this idea, but I'm worried about having to put disclaimers next to certain features about 'if you use this feature, the compiled code will not work in Internet Explorer'. Unless something's changed, there aren't official browsers that Svelte commits to supporting, but in the past we've aimed to produce code that runs in the last few versions of IE.

Rich-Harris commented 7 years ago

That REPL example is super cool! I actually think that's already quite nice and usable. May even start using it in a few places where I don't need to support IE.

glam manages to polyfill CSS vars somehow. Haven't studied the implementation, but maybe there's something in there that Svelte could be doing. (No idea.)

If we did want a more concise approach, I think it's number 4:

width:  {{40 * count}}px;

The implementation certainly could get a bit hairy, especially if polyfilling was involved, but I don't see a huge amount of value in the intermediate versions over and above the example that works today.

colah commented 7 years ago

@Conduitry: that's a very reasonable reservation to have.

According to this blog post, Microsoft now supports CSS custom properties in their "Windows Insider Release". And the blog post is by the program manager for Edge and has a very positive tone, so I'd read that as a pretty strong signal that they intend to support it. No idea how long it takes features to move from their insider release to public consumption, let alone to trickle out to wide spread use.

Rich-Harris commented 7 years ago

One other consideration — csstree won't parse this:

div {
  color: {{color}};
}

Just to make things a bit more annoying!

Conduitry commented 6 years ago

It might be neat to revisit this, now that we're no longer supporting IE11. It also looks like recent versions of csstree are able to parse CSS that contains mustaches/tags.

colah commented 6 years ago

For what it's worth, this would still be very exciting to me!

My svelte code constantly turns into tags with style="multiple lines of css styling with mustaches" that's not very readable. It really feels like that this would make it much cleaner!

saabi commented 5 years ago

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>
michalczaplinski commented 5 years ago

@Rich-Harris Is this still a desirable feature? As per @colah's solution number 4:

width:  {{40 * count}}px;

I'd like to send a PR because I'm missing this coming from styled-components.

Also, could you clarify your comment there https://github.com/sveltejs/svelte/issues/1687#issuecomment-415962961 :

(To clarify: no, you can't use component state in `

bluwy commented 3 years ago

I've made https://github.com/sveltejs/rfcs/pull/51 to bring this discussion back. I don't think https://github.com/sveltejs/rfcs/pull/13 really answered this issue as it only highlights passing CSS variables from parent to children, in contrast, this issue discusses about passing CSS variables within a component.

Glad to take any feedback.

aradalvand commented 2 years ago

Setting CSS custom properties on elements via actions has one major downside which is that it won't work with SSR. I should this issue should be reopened.