-
Notifications
You must be signed in to change notification settings - Fork 82
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
Style directives #42
Style directives #42
Conversation
pretty interesting. i wonder what other attributes than |
I like this and think we should do it. Re conflicts, <div style="color: {color}; font-size: {size}px" style:color="blue"> ...we can and do extract the individual <script>
export let color;
export let size;
$: styles = `color: ${color}; font-size: ${size}px`;
</script>
<div style="{styles}" style:color="blue"> since we have to bail out of the optimisation and resort to setting a) deal with it — we're already bailing out of the optimisation a) is probably more philosophically consistent with the rest of Svelte, though perhaps it should generate a warning. Personally I'm inclined not to do the camelCase thing, because we don't do that anywhere else, we force you to be explicit: <div class:is-active={isActive}> It could be argued that styles are different because these are actual properties of |
I wonder if we could also support react style directive, that is to also support something like: const style = { marginLeft: "30px", backgroundColor: "red" }
</script>
[...}
<div style={style} /> or also the common shortcut I'm not so sure if both syntax might be compatible... |
It's a good idea. I made a custom preprocessor sometime back to support style directive. https://github.com/sidx1024/svelte-style-directive |
How would we get this working with custom css properties ? |
@opensas This isn't relevant to this RFC. |
Looks ok to me: https://svelte.dev/repl/b862199691df49e9b2e9c68c707f249b?version=3.31.0 |
@stephane-vanraes @antony Yeah it is valid syntactically. Even if it weren't, we could explicitly support it as we control the parser and most of our tooling. |
I think we can all agree that setting style properties dynamically through raw css strings is unpleasant With that said the proposed solution has minor drawbacks
I'd suggest going for something much easier, that would be for the style attribute to simply support inline object literals as an input, thus enabling the following syntax <div style={{ 'padding-left': (offset + 2) * 16 + 'px' }} /> To make it easy for everyone, here's a dirt cheap way to also support camelCased properties if (property in node.style) node.style[property] = value;
else node.style.setProperty(property, value) On a side note I would like to underline that inline style is better left to dynamic styling, as all examples in the RFC are either constant or most fit for the class directive |
I think the strength of this proposal lies in making it easier for conditional properties
vs
|
You don't need an RFC for that, and it's not easier to read than: <div style:padding-left={(offset + 2) * 16 + 'px'}>
it's only boilerplate if you require it. It's syntactic sugar, and it's optional, if you want to specify a dynamic style property
I disagree, because |
I don't think the clash will have a huge impact because the same is true of class attributes and directives and that is a very popular construct that gets used extensively and often together. There is an issue of precedence but that has already been mentioned. |
just to clarify about not needing an RFC: https://svelte.dev/repl/c7787db098e6495888036598586ec4f3?version=3.31.0 I did originally thing just passing and reading a style prop would be fine but then I realised that will only work for components. |
That was badly expressed, the problem is, again, the combination with conditionals. This construct would be very nice to pass on custom variables:
In this case the shorthand would not work because |
What do you think about an idea of allowing to pass styles to a component, and, inside it, set styles to tags, like you can do now with events (e.g., with on:click): //App.svelte
<SomeComponent style:transform={"translate(20px, 20px)"} />
<AnotherComponent on:click={() => console.log("Clicked")}/> // SomeComponent.svelte
<div style>Text</div>
//style:all ?
//style:transform ? //AnotherComponent.svelte
<div on:click>Click me</div> |
It seems to me that with this implementation it can be done... |
It would also be nice if the tags would contain validation where possible.
|
What with CSS Typed Object Model? With Svelte modifiers, it could look like this: Props:
The first and second propositions might look like this: I do not hide that the third proposition seems to be the most interesting. :) Additionally - Support for more than one property:
|
This could be a really nice way to make CSS transforms easier to work with. .foo {
transform: rotate(15deg) transalate(-20px,0px)
} could become <div
style:transform-rotate={'15deg'}
style:transform-translate={'-20px 0px'}
/> That would be really neat for making them dynamic, and maybe there can be syntactic sugar for dealing with certain units. <div
style:transform-rotate-deg={calculateRotation()}
style:transform-translate-y={calculateY()}
/> |
We would really be building ourselves a maintenance headache there. Every time the CSS spec changes we'd need to add a bunch of transformations. It does indeed look nice, but i would keep it simple to avoid a lot of conversion and mysterious syntax which would require documentation:
|
Any syntax here needs to have a 1-2-1 mapping to the CSS spec as much as possible. This should be a generic abstraction on top of CSS that makes learnability straightforward by following a simple pattern. Lots of complex exceptions would make this more difficult to learn and more difficult to maintain over time. |
I do love the terse-ness and readability of this, but what happens if you do this? <!-- is this a syntax error? would using the directive modifiers imply an array input? -->
<span style:text-shadow|px|px={2}; />
You can get something fairly readable just by using template strings: <span style:text-shadow={`${2}px ${2}px #FF0000`}; /> EDIT: Also, this doesn't allow for a syntax that would allow you to dynamically alter the CSS unit type of the input you're feeding in, unfortunately. |
Rather, a warning is enough. |
Further to my previous comment, this discussion is basically why we would never implement this kind of syntax. |
@pngwn Who are you addressing? |
Anyone discussing syntax that doesn't observe this rule:
|
True, seeing examples using template strings seems to be less verbose anyway. <div
style:transform-rotate={'15deg'}
style:transform-translate={'-20px 0px'}
/> becomes
|
You don't even need template strings — instead of <div style:transform={`rotate(${rotation}deg) translate(${x}px ${y}px)`}/> you can do <div style:transform="rotate({rotation}deg) translate({x}px {y}px)"/> |
On the topic of accepting object literals: While I'd personally be happy if the I'd also note that if one does want to construct style attribute strings from object literals, a relatively simple helper function can be used (for example here): <script>
import styleToCss from 'style-obj-to-css-string';
export let maybeNull = null;
export let width = 40;
export let coolVariable = "goldenrod";
export let otherStyle = {};
$: style = styleToCss({
position: maybeNull && "absolute",
width: `${width}px`,
"--my-cool-variable": coolVariable,
...otherStyle
});
// `style` is: "width: 40px; --my-cool-variable: goldenrod;"
</script>
<div {style}>hello</div> In any case, it's something that could be discussed in a separate RFC, as it's not mutually exclusive with the current proposal. |
My main issue with this is that many dynamic styles become directive soup. What do you think about compiling to CSS variables (custom properties) as opposed to style directives? <script>
export let maybeNull = null;
export let width = 40;
export let coolVariable = "goldenrod";
</script>
<div>
hello
</div>
<style>
div {
position: {maybeNull && "absolute"};
width: {width (100)}px; /* 100px fallback? */
--my-cool-variable: {coolVariable};
}
</style> which would compile to have equivalent behaviour to custom properties: <script>
export let maybeNull = null;
export let width = 40;
export let coolVariable = "goldenrod";
</script>
<div
style="--svelte-hash1: {maybeNull && "absolute"}; --svelte-hash2: {width}px; --my-cool-variable: {coolVariable};>
hello
</div>
<style>
div {
position: var(--svelte-hash1); /* Ignored if blank */
width: var(--svelte-hash2, 100px /* fallback */);
}
</style> Dynamic styles can then be reused more easily than with a Maybe this warrants an RFC of its own. |
@kwangure allowing JS variables in CSS would be a rather huge paradigm shift — and the idea of mixing JS and CSS (even if it's only at the compiler level) is generally discouraged as going against the philosophical grain of Svelte. |
On the contrary, it feels so well aligned with Svelte's philosophy to me. Custom properties enable shifting dynamic and repeated CSS values without messing with CSS. Svelte could make this even easier. And yes, it's a paradigm shift —but that's a good thing. "Paradigm shift" characterizes many features I like about Svelte. My only concern with my suggestion is the feasibility. A svelte CSS block (and by extension custom properties) is shared among several component instances, but changing state in one component instance should not affect the styling of another downstream. If it were possible to solve this through some compiler magic (I don't have ideas yet), then I think this definitely feature is up Svelte's wheelhouse. That said, my concern here was around having a |
@kwangure using JS variable in the CSS will also make it (nearly) impossible to extract the CSS to it's own file without adding a fair amount of complexity related to updating those styles in the compiled code, probably not entirely impossible. But as you say their still will need to be a system to differentiate instances from each other. The "easiest" way to this is to then lift out the css properties that use js variables during compilation and inline them (basically have the compiler do the littering) Perhaps a precompiler could do this for you instead <script>
let height = 100
</script>
<div>Test</div>
<style>
div {
color: red;
height: {height}px;
}
</style> would then be 'precompiled' to <script>
let height = 100
</script>
<div style="height: {height}px">Test</div>
<style>
div {
color: red;
}
</style> |
I seem to be running against the crowd here, but this feels like unnecessary sugar to me. Inline styles are already well established, and with Svelte's existing string templating conditionals in them behave consistently and as expected. This just introduces yet another API to the Svelte DSL for new developers to become accustomed to, and can lead to messy Other than the saved keystrokes, is there any practical benefit to this RFC that I'm missing? |
merging, since #5923 is good to go |
is this ready to give it a try? |
sveltejs/svelte#5923 |
What was the final verdict on camel casing? <div style:maxWidth /> Not supported and won't be coming in the future? |
Rendered