-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Javascript Interpolation in Styles via CSS Variables? #758
Comments
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. |
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. |
@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. |
One other consideration — csstree won't parse this: div {
color: {{color}};
} Just to make things a bit more annoying! |
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. |
For what it's worth, this would still be very exciting to me! My svelte code constantly turns into tags with |
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> |
@Rich-Harris Is this still a desirable feature? As per @colah's solution number 4:
I'd like to send a PR because I'm missing this coming from styled-components. Also, could you clarify your comment there #1687 (comment) :
Perhaps I misunderstood, but does this mean that the compiler would have to create a copy of the styles for each component instance if we interpolate JS in PS. Just started using Svelte a couple days ago and, wow, I'm super impressed. Great work! 🙂 |
With the current CSS implementation, yes. There is one CSS file for each component, not for each component instance. In the above example you could have several instances of a component with several The only way to make this work would be for the compiler to check which values were dynamic and manage those values in the component instance rather than in the CSS file. There is more to think about here as well (can you interpolate which CSS is definitely something that we want to improve, so I don't mean to be negative but there are some important things to consider before implementing these kinds of changes both from a design point of view and a technical point of view. |
@pngwn Thanks for the explanation - that makes sense. I ll have a look at how other libs deal with it and try to come up with some ideas. |
<script>
import { css } from 'emotion';
export let width = 200;
$: boxStyle = css`
border: solid 1px red;
height: 200px;
width: ${width} px;
font-size: 1em;
white-space: nowrap;
`;
</script>
<style>
h1 {
color: red;
}
</style>
<div class={boxStyle}>
<h1>Hello {width}!</h1>
</div> styled component like in Svelte! @Rich-Harris first: great works! Some tips to make Svelte a real-big-apps alternative to React :
<script>
import { css } from 'emotion';
export let width = 200;
let boxStyle = css`
border: solid 1px red;
height: 200px;
width: ${width} px;
font-size: 1em;
white-space: nowrap;
`;
</script>
<div class={boxStyle}>
<h1>Hello {width}!</h1>
</div> again: great works! :D |
Some POC i'm testing with emotion as css runtime : App.svelte <script>
import { MyDiv, MyButton } from './styles';
let width = 200;
let toggleButton = true;
let toggleDiv = true;
</script>
<input type="number" bind:value={width}>
<MyDiv width={width} on:click={()=> { toggleDiv=!toggleDiv}} >
Toggle Me
</MyDiv>
<MyButton width={width-40} primary={toggleButton} on:click={()=> {toggleButton=!toggleButton }}/>
<MyDiv width={width}>
Button toggle result : {toggleButton}
Div toggle result : {toggleDiv}
</MyDiv> styles.js import { styled } from "svelte-styled-components";
export const MyDiv = styled.div`
border: solid 1px red;
height: 200px;
width: ${props => props.width}px;
flex-direction: column;
display: flex;
cursor:pointer
`;
export const MyButton = styled.button`
margin-top:20px;
background-color: ${props => (props.primary ? 'green' : 'blue')};
height: 25px;
width: ${props => props.width || 80}px;
`; https://codesandbox.io/s/l3n6w3lwom feedback? |
What if make CSS react on variable changes like this? <script>
let myVariable = '#000'; // mentioned in CSS
let anotherVariable; // not mentioned in CSS
// The next should be generated by the compiler to sync variable with CSS in reactive way
let css = componentElement.style; // don't know how to get this
$: {
css.setProperty('--myVariable', myVariable);
}
</script>
<style>
div {
color: var(--myVariable);
}
</style>
<div>Component content</div> |
I think this represents functionally what I'm hoping we could accomplish: With it generated from: <script>
let color = 'pink';
</script>
<style>
h1 {
background-color: var(--color);
}
</style>
<h1>Hello world!</h1> I'm starting to slog my way through the code to see if I can start figuring out how to make this happen, but I'm new here. 😄 |
I'd expect custom properties to be scoped by default just like everything else. Then we can generate compile errors if a variable is referenced that doesn't exist. We'd want to provide a way to reference globals... something like this?: background-color: :global(var(--color)) |
Maybe there’s a path forward through Constructable Stylesheets? https://developers.google.com/web/updates/2019/02/constructable-stylesheets |
You can achieve CSS/JS interoperability with PostCSS; see here for an example with Sapper. ...for the flip-side: <script>
import { customProperties as style } from '../styles/variables';
console.log(style['--theme']);
// #=> '#ff3e00'
</script> |
I just released a tiny package that tries to solve this problem: https://github.com/kaisermann/svelte-css-vars. It uses an action to automatically do those REPL: https://svelte.dev/repl/1522fe3bdf904843a01101d9f900241d?version=3.8.1 |
@kaisermann Great job.... when i saw @seantimm comment... i have an idea... but you already make it... very good |
Hi everyone, came from React/Gatsby world and building my next client site with Svelte/Sapper. This might be Sapper-specific but it seems the proper thread to ask. I have a global style: :root {
--color-accent: rebeccapurple;
}
<style>
nav {
color: var(--color-accent);
}
</style>
<nav>
...
</nav> Now, this is a client site and they carry different brands, thus different routes should be able to override this accent color:
<script context="module">
export async function preload({ params }) {
const res = this.fetch(fetchAccordingToParam)
const data = await res.json()
if (res.status === 200) {
return {
accentColor: data.accentColor,
}
} else {
this.error(res.status, data.message)
}
}
</script>
<style>
:global(:root) {
--color-accent: accentColor_override_from_preload; // <--
}
</style> How can I achieve the above? I’ve tried using stores, but that causes the color to flicker (the override isn’t server-rendered, I prefer it done SSR). As last resort, I could make Nav a child of route, but first I would love to hear if anyone has a solution. Another angle to this question is: can data from route’s preload function be made available to _layout? Thanks. |
@ryanditjia I don't think this is possible in an SSR context (see #917). You may be able, in your root layout's |
Moving discussion to sveltejs/rfcs#13 |
We also have this CSS.registerProperty method: <script>
window.CSS.registerProperty({
name: '--my-color',
syntax: '<color>',
inherits: false,
initialValue: '#c0ffee'
})
</script>
<style>
h1 {
background-color: var(--my-color);
}
</style>
<h1>Hello world!</h1> only works in Blink doe... maybe there is a polyfill for that |
Please add support; |
I've made sveltejs/rfcs#51 to bring this discussion back. I don't think sveltejs/rfcs#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. |
Setting CSS custom properties on elements via actions has one major downside which is that it won't work with SSR. |
Back in November, there was some discussion of whether svelte could support javascript variable interpolation in style sheets (sveltejs/v2.svelte.dev#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:
This sort of pattern might be attractive for a a few reasons:
:hover
).(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.
--count
:Or maybe this, if you wanted to still remind the user that it's a variable:
Which would compile into something like:
Potential Downsides
On the flip side, it may make some things more transparent -- especially if we can keep variable names synced to javascript.
The text was updated successfully, but these errors were encountered: