-
Notifications
You must be signed in to change notification settings - Fork 309
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
feat: support reactive themes up to 4 levels deep #761
base: main
Are you sure you want to change the base?
Conversation
workflow: benchmarks/sizeComparison of minified (terser) and compressed (brotli) size results, measured in bytes. Smaller is better.
|
I think this is back to creating a situation where themes get blended. That is a complexity that is best avoided IMO. The example provided is essentially 2 theme partials being nested. In practice people will create one big theme that's an array of theme partials. If the API worked as you described, then part of that theme could be overridden within the subtree, creating a blend of themes rather than mutually exclusive themes |
@necolas I think I failed to explain the work correctly in the description above. There should NO blending of themes. There should only ever be exactly ONE theme whose values are active. The work here is only about dealing with the situation discussed in #601 where variables I still have to make the changes to Now, there are some edge-cases I'm thinking through and it's possible this solution won't work out, but we're on the same page about how the themes should work. Update: You're right. This approach won't work. However, I do have another path that I think should work. |
fixes #601
This PR finally implements reactive themes where variables work the way you expect. However, as a practical limitation, it only supports 4 layers of nested themes for a
VarGroup
that uses values from a separateVarGroup
to define themes.This can be a bit hard to explain, so here's an example.
Say there are two
VarGroups
,globalTokens
andpageTokens
:And there are themes for these two
VarGroups
as well:Now consider the following usage examples:
In this first example because the theme for
globalTokens
is applied higher up the tree than thepageTokens
, everything works as expected. WhenpageTheme1
is applied, it resolves to the values fromredGlobals
as they are already available.Now, here is the example where we can see the updated behaviour:
Here, the
pageTheme
is applied higher up the tree. The way that CSS works, this would mean that it set thepageTokens
to values from the default values ofglobalTokens
, which is "black" and "lightgrey".However, when
redGlobals
is applied further down the tree, most devs expect the variables to behave reactively and expect the "pageTokens" that depend on "globalTokens" to resolve to the nearest value.After this PR, they do.
How does this work?
Relevant Background
Every
.defineVars
call creates a unique className for the set of variables. Let's assume the classNames are ".globalTokens" and ".pageTokens" respectively. (The real classNames are hashes).Every
.createTheme
call uses this className alongside the className created for the generated theme. Let's assume that:redGlobals
sets the classNames"redGlobals globalTokens"
pageTheme1
sets the classNames"pageTheme1 pageTokens"
NOTE: the themes return TWO classNames each. This is how any variables that are omitted in the theme are automatically set to their default values from the
defineVars
call.Dealing with "dependencies"
This PR adds code to track the "dependencies" of a
Theme
. In the example above,pageTheme1
depends on values fromglobalTokens
.So to ensure that once
pageTheme1
is applied it always uses the most "current" value forglobalTokens
, CSS descendent selectors are used:Since any
Theme
forglobalTokens
will also apply the.globalTokens
className, we can automatically re-applypageTheme1
wheneverglobalTokens
is themed further down the tree.Accounting for nested themes
So far, we only considered the case where there is only a single theme for
pageTokens
being applied. However, it is possible that there are many themes for the sameVarGroup
that are applied on nested elements:With the CSS selector above, we would end up applying the styles for all three
pageToken
themes. What we want is the inner-most theme forpageTokens
to take precedence. Currently, the only way in CSS to create a so-called "nearest parent" selector is by using the new feature@scope
.@scope
is a very new feature that is not even supported in Firefox yet. Being an at-rule, it also necessitates repeating the entire CSS again.As a practical workaround, this PR implements the feature for upto 4 nested themes, and implements them with bigger selectors that will win via specificity:
Since every theme for
pageTokens
will also set the classNamepageTokens
, every time you nest a theme for the sameVarGroup
, a selector with a higher specificity will win.This means that as long as you don't nest themes for the same
VarGroup
more than four times, things will work as expected.No unnecessary complexity
The compiler detects when CSS variables from another
VarGroup
are used when defining the values of a theme. It only adds this complexity to the CSS when it is needed. If you don't define variables that point to other variables, no additional complex selectors will be created.What's next?
This PR has implemented the feature for
createTheme
, but the same feature is also needed fordefineVars
itself. We need to account for the default values of aVarGroup
itself depending on variables from anotherVarGroup
.