Replies: 10 comments 26 replies
-
|
This is very cool! Both the type safety and the passing parameters as css custom props (if we can parse them correctly). For the latter, we'd still have to create some kind of wrapper to be able to apply the css prop. scoped css props with an ability to use |
Beta Was this translation helpful? Give feedback.
-
|
Not a fan of the custom css syntax i.e. First idea seems awesome to me. |
Beta Was this translation helpful? Give feedback.
-
That was my initial thought as well, although I'm not a big fan of using (Kind of) Like in Svelte? But that would mean <Child style={{ '--color': @color }} />
<Child style:--color={@color} />
<Child --color={@color} />Like in Vue? But that requires the child to declare props it wants to bind to CSS: <!-- Parent -->
<Child :color />
<!-- Child -->
<script setup lang="ts">
defineProps<{
color: string;
}>();
</script>
<div class="text">Text</div>
<style scoped>
.text {
color: v-bind(color);
}
</style>Both of these seem more verbose than this: <Child class={#style.child(@color)} />Especially if there are multiple variables: <Child class={#style.child(@color, @bg, @border, @outline)} />It would be the other way around, more verbose this way, if you wanted to assign multiple classes to a child that use the same colors, but I think it's not a very common situation: <Child class={[
#style.child(@color, @bg, @border, @outline),
#style.otherClass(@color, @bg, @border, @outline),
#style.thirdClass(@color, @bg, @border, @outline),
]} />So, overall, the solution from the OP seems the cleanest to me. |
Beta Was this translation helpful? Give feedback.
-
|
Is there a reason why we can't just allow for multiple style tags? i think the more the syntax changes the more needs to be learnt and the less familiar the language/framework becomes. If we would allow for style tags under all elements we can generate a new unique class for each tag which would enable much flexibility for targeting children different ways. heres an example of how it can look:
the prop "rippleClass" would be a placeholder for a unique class generated during comp, and that property gets passed into any element that has a style tag or is targeted. additionally, the styles with "{ ... }" will be compiled to ".unique-class { ... }". so you won't need to add arbitrary classnames just to apply the styles to the element being targeted |
Beta Was this translation helpful? Give feedback.
-
|
Personally, I think we should go ahead with part 1: component App() {
<Child class={#style.child} />
<style>
.child {
color: red;
}
</style>
}This is the MVP and will likely unlock the compat problem we currently have with using Ripple with React. So I think we should go ahead and implement this – otherwise there's no real easy way to pass styles in Ripple to React components. |
Beta Was this translation helpful? Give feedback.
-
|
For me personally, we can go with the idea of #style at first. We anyway iterate quickly so let's first unlock compat layer and then later on we can eventually change it for the better suited approach. I'm not a fan of an additional property (#style) as of global/root style, because we are going away from our idea of being simple. Additional custom property means additional learning curve and difference what people already know. But let's start from something and then see how it goes. @trueadm |
Beta Was this translation helpful? Give feedback.
-
|
Is it possible to do it on a component level like Mycomponent.style.classname or in the case of your example could do this.style.classname so you could import styles from other components and it would read a bit more javascript without any magic #? |
Beta Was this translation helpful? Give feedback.
-
|
I really liked the original idea of having a feature where css custom properties can be connected to a component's tracked variables. As an alternative to using a "function" syntax, we can use Each declaration needs to specify a default and use the css Users can create global or class scoped custom prop declarations, inside But regardless, all ripple-managed declarations end up being created in the The user-defined declaration stay where they were declared and get set to the ripple-managed uniquely named declarations. We basically set up an internal effect to track changes of each of the referenced component tracked variables and once they change, we change the ripple-managed uniquely named custom properties, e.g. It seems like a pretty simple, flexible and familiar syntax and users can still use the already implemented Here's a code example: component App() {
// variables used by style have to reside in the top scope
let color = track('red');
let someOther = track('blue');
<Child class={#style.test} />
<button onClick={() => { @color = @color === 'red' ? 'black' : 'red' }}>{'Change color'}</button>
<style>
/* has to come from the component's top scope */
:root {
--somecolor: var(#component.@color, red);
}
.test {
--bgcolor: var(#component.@someOther, blue);
color: var(--somecolor);
background-color: var(--bgcolor);
}
</style>
}
component Child(props) {
<div {...props}>{'Child'}</div>
}transformed to css: <style>
:root {
/* all ripple-managed declarations go into `:root` with unique var names */
--ripple-1ikjjlr-1-somecolor: red;
--ripple-1ikjjlr-2-bgcolor: blue;
/* set the global user var to the managed */
--somecolor: var(--ripple-1ikjjlr-1-somecolor);
}
/* scoped to the class selector */
.test.ripple-1ikjjlr {
--bg-color: var(--ripple-1ikjjlr-2-bgcolor);
color: var(--somecolor);
background-color: var(--bgcolor);
}
</style>and js: _$_.effect(
var __a = _$_.get(color);
// this will be wrapped in a function
document.documentElement.style.setProperty('--ripple-1ikjjlr-1-somecolor', __a);
);
_$_.effect(
var __b = _$_.get(someOther);
// this will be wrapped in a function
document.documentElement.style.setProperty('--ripple-1ikjjlr-2-bgcolor', __b);
); During compilation we perform:
|
Beta Was this translation helpful? Give feedback.
-
|
Yeah, we definitely need a default value to set the custom css prop. Our build step, as well as vite, places the css into a separate file which gets placed into the We could set it to If there is no default than the browser falls back to the previous valid value in the cascade, eventually reaching the browser default, e.g. for We could rely on the user to set the default when they use We could allow the user to declare the css custom property inside the We could make the default optional and let the user decide but I'm afraid most won't be aware of the fouc consequences. To be fair, it still could occur if the provided default is different from the tracked value on load. I'm good if you want to allow both syntaxes: I'm also good if we want to allow the shorthand, although it's less readable to me:
But again, I'm totally open to going with So, the resulting spec, except for the final decisions on the syntax for the default, would be the following: style source code: <style --color={[color, 'red']}> /* or --color={color ?? 'red'} or --color={color || 'red'} or --color={color} or {color}> */
.test {
color: var(--color);
}
</style>style compiled code: <style>
:root {
/* set a uniquely named var with the default on the document for fast access and modification */
--ripple-1ikjjlr-color: red;
}
.ripple-1ikjjlr {
/* create a scoped custom prop that only apples to the scoped styles linked to the one in the `:root` */
--color: var(--ripple-1ikjjlr-color);
}
/* user compiled code, same as today */
.test.ripple-1ikjjlr {
color: var(--color);
}
</style>the tracking js is compiled to something like this: _$_.effect(
var __a = _$_.get(color);
// this will be wrapped in a function
document.documentElement.style.setProperty('--ripple-1ikjjlr-color', __a);
); |
Beta Was this translation helpful? Give feedback.
-
|
Ok, I had time to sleep on it, reflect and simplify. We should definitely separate container elements from the stylesheet implementation. Container element handling should be either done by users or we can come up with something like what svelte did. We're going with the style prop syntax where we can have a few variations and i think we can and should support them all: <style
{color} /* shorthand, no `--`, no default value */
color={something} /* longhand, no `--`, no default value */
--color={something} /* `--` in front, always longhand, no default */
color={[color, 'red']} /* longhand, no `--`, with a default value */
--color={[color, 'red']} /* `--` in front, always longhand, with a default */
</style>Here's an example to illustrate the spec: <style --color={[color, 'red']} {font} --global-margin={margin} {padding} border={cssBorder}>
:root {
--global-margin: 6px;
}
.test {
color: var(--color);
padding: var(--padding);
border: var(--border, 1px solid grey);
--font: 12px;
}
.test :global(button) {
color: 'red';
}
</style>transformed to: <style>
:root {
--ripple-1ikjjlr-global-margin: 6px;
--ripple-1ikjjlr-color: red;
--ripple-1ikjjlr-padding: initial;
--ripple-1ikjjlr-border: 1px solid grey;
--ripple-1ikjjlr-font: 12px;
--global-margin: var(--ripple-1ikjjlr-global-margin);
}
.test {
color: var(--ripple-1ikjjlr-color);
padding: var(--padding);
border: var(--ripple-1ikjjlr-border);
--font: var(--ripple-1ikjjlr-font);
}
.test :global(button) {
color: var(--ripple-1ikjjlr-color);
}
</style>The result is:
Wins:
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
One thing that is critical to styling libraries, is the ability to "pass" or "forward" styles between components. Today, it's not easy to do that, but imagine if we could use our
#syntax to make this happen. Essentially, you can use#style.{cssClassName}to reference a style in your scoped<style>block and pass that on to another component – to be used as aclassName.The Ripple TS compiler would ensure that this also type-safe – so trying to apply a className style that isn't in
<style>would result in an error.Then it got me thinking – maybe this is how we could also apply reactive dynamic styling too via CSS variables?
Beta Was this translation helpful? Give feedback.
All reactions