Replies: 2 comments
-
did you know about regarding the |
Beta Was this translation helpful? Give feedback.
-
@astahmer thank you for the suggestions! The The main feature here is having an API which a root slot is able to control the style of it's descendents, without the need of using context. It's not multiple CVAs because there is a clear relationship between them. The SVA itself is multiple CVAs from what I can see on the code, what I'm proposing is a relationship and hierarchy between slots, which is a very common SVA use-case and implicit on the API but Panda doesn't handle it. |
Beta Was this translation helpful? Give feedback.
-
Currently on SVA, in order to have styles that changed based on parent slot properties, we need to add a context and pass down the generated class names. Although this is the best option for React, I believe this could be solved completely by CSS (with multiple options available).
At the same time, solving it with CSS-only, would break the atomic part of SVA (that's why I believe maybe a new API is more adequate). Let me explain the problem I have and which are the possible solutions I see.
The problem:
In a Design System, it's very common to have complex compositions, where the children styles depends directly on the properties of the parent (what SVA tries to solve).
In my Design System, I don't use config recipes. Why? Because we use monorepo and each component family is a different package, this is designed this way so when we have changes, we only need to run the relevant tasks for the changed package (using NX).
Using config recipes would require to retest all the components for a simple CSS change. Also, the config recipes make the code "too far" from the component, we chose to use CVA and SVA, which better work for our case (together with styled).
In an example Button component, we can have something like this:
The label/icon size and color depends directly from the properties
size
andvariant
from theButton
component, where SVA are perfectly applicable. But this is also possible with raw CSS without any need to bind the component to a context, as the CSS variables can be scoped to a parent root and consumed on a children (the same as a React Context), although, it would break completely or partially the atomic CSS concept.Possible solutions
Use CSS variables for the variable part of a children slot
On this option, the base styles for all the slots keep atomic and working as currently. If a CSS property is on the variants, we would replace it's value by a CSS variable. They can keep atomic but they will be stable, as their value is changed through changing the CSS variable value -- Making possible to be used on any framework without context-like structure.
To provide the CSS variable values, we would need to generate classes with each one of the values combinations, based on the active variant, those would be added to the class list of the "root slot" (a new concept to be added to the SVA). With this, we can also make SVA work with styled.
Looking into the SVA API changes for the previous example, we would have something like:
Using the API above, we would have the following results:
buttonRecipe();
Outputs:
buttonRecipe({ size: 'sm' });
Outputs:
buttonRecipe({ size: 'sm', variant: 'secondary' });
Outputs:
On CSS, we would have the following additional rules:
All the possible variants are generated on the CSS parsing time. The variable naming structure is
--{className}-{slot}-{property}
, so they can still be atomic and scoped, at the same time, they can be recreated by the generated code, as they have all the required attributes to generate them. For composed variants, a new class can be created merging the values and declared after on CSS to avoid specificities issues.This option creates little disruption to the atomic design of SVA but it still breaks the atomicity of the styles, by having to create the classes with the variables for the children.
Create real class names using another API than the configuration
In this option, we don't use variables but selectors to toggle the values for the children automatically. Each slot would have their own class generated, then we could target them automatically based on the parent classes.
This is a simpler approach but currently only Config Recipes are able to generate class names. On this case, I believe a new API would need to be created to support this case, to avoid conflicting with the SVA concept (as there is no atomicity on it). The API can keep very similar to the prior approach but generating a different result.
Usage example:
Using the API above, we would have the following results:
buttonRecipe();
Outputs:
buttonRecipe({ size: 'sm' });
Outputs:
buttonRecipe({ size: 'sm', variant: 'secondary' });
Outputs:
On CSS, we would have the following rules:
On the CSS output we have the following logic:
.{className}-{slotName}
, if not the root slot. On the root slot, we add just the.{className}
.{className}__{variant}__{variantValue} .{className}--{slotName}
Use variables and container queries to change the style
This option is an iteration over the prior option, which we can avoid the chained styles by using container queries. Each variant would have their own CSS variable and we would toggle its value based on the current value. Then, each variable style can use a container query to identify which style to apply.
The style query from a container still experimental and not available in most browsers. I'd not consider this option for the foreseeable future but it could be a better option.
The API is the same of prior option, with same results, changing only the CSS output:
styled()
IntegrationCurrently, SVA doesn't work with
styled()
, which I believe is related to the problem of having to declare the provider or know the variable values to be able to generate each atomic style.As with the APIs above we know which is the
rootSlot
(the one which receives variables) and the other slots are stable, we have how to integrate it with thestyled()
API.On the consumer side:
For the root slot (e.g.
buttonRecipe.slot('root')
), it would return something similar to:For the other slots (e.g.
buttonRecipe.slot('label')
), it would return something similar to:On the generated
styled()
, it would do 2 different treatments based on if it's a root slot or not:For root slot
When
isRoot: true
, need to run thesva
function with the property values in order to generate the className to the final component.For non-root slots
When
isRoot: false
, we pick up the value fromclassNames
and apply it directly, as we know it will be stable for those.Please feed back if this feature improvement makes sense and I can even contribute into this change.
Thank you!
Beta Was this translation helpful? Give feedback.
All reactions