-
-
Notifications
You must be signed in to change notification settings - Fork 366
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
Suggested implementation of themes refactoring #1340
base: main
Are you sure you want to change the base?
Suggested implementation of themes refactoring #1340
Conversation
e65a3f0
to
f9027ce
Compare
Approach makes sense to me. Thank you.
Making all the tokens required in |
Great :)
So to be 100% clear, you mean at least this?
But do you also mean this?
I'd argue if we think those component-specific variables are kinda legacy and we eventually want to remove them, I'd say making them required in a theme object is maybe too much trouble? Since the idea is themes would not want to change those, as Here are a few thoughts:
Yeah I guess this way of doing was more to show the structure, indeed "grist-tokens" is not necessary. I'd argue if *everything* is listed in a theme object, the concept of "grist-base" variables is also debatable: every design token variable is tied to the theme, and then there is the custom css layer that comes on top. |
Only the variables we'd like to change from theme to theme. So yes to colors, but no to z-indexes. Padding and font size are more interesting; I can see an argument that making them required would be more of a nuisance, as they won't always change across themes. A compromise might be to make the vars (e.g. fonts, padding, etc.) optional, and the colors required, with the vars being inherited from the base layer.
No, I agree that it's too much trouble to make those required.
I had a similar thing in mind. A base theme defining the default, non-color variables makes a lot of sense to me. The current system is awkward because things live in two places, as you said. If we can narrow that down to one, that'd be great! |
Hey @georgegevoian, Here is an update to move things forward. SummaryThe idea is:
Feedback neededVariable namings and impact on widget's stylingI made the choice to abstract the css variable names as much as possible. The goal is that we don't have to regularly switch in our heads between the consumed in the codebase name ( So there is some code to map some js object keys to existing css var names. Unfortunately I only figured out later that theme variables in widget iframes was loaded differently. And from what I understand I can't import common files in the plugin-related code? Which means for now I can't go find the css variable name matching the theme key in the plugin code, like I do in the default Any advice on what I could do there? My intuition would be to actually remove the js object key <> css var name manual mapping and just generate css variable names based on the JS keys. So that TS checkersI had trouble with the ts-interface-checker related code, because it doesn't handle generics. I ended up just… removing the I'm not against a little bit of explanation here if what I did is not ok, I assume I missed something :) Next stepsIf what you see seems globally find for you, here what I wanted to continue on:
What do you think? Thanks a lot. |
Sounds good. One small suggestion - and only if it's simple to implement - would be to split the exported import {colors, fontSizes, legacyVariables} from 'app/common/ThemePrefs';
export const button = styled('button', `
font-size: ${fontSizes.medium};
color: ${colors.slate};
background-color: ${legacyVariables.controlPrimaryBg};
`); How does that look to you? I like it a bit better, but I'm also fine with how you've done it now.
Ok to remove and not worry about, for now. It was done very early on for the (ambitious) goal of supporting custom themes (where users could specify the values of individual variables). The checkers alone aren't sufficient for validation anyway (they don't actually check the values are valid CSS colors).
For preserving backward compatibility, it's really just the properties in The easiest fix for the plugin theme handling would be to move whatever theme-related code you need to
Actually, one big commit may be problematic because this repo doesn't contain all the UI code bundled in the SaaS build of Grist. Let's plan on having 2 ways of consuming tokens, with the aim of getting all the code using the old way migrated over fairly quickly in follow-up PRs. That'll give us some time to update any SaaS-only code to use the new variables. Besides that, I think your plan for next steps sounds good. Thanks @manuhabitela. |
Thanks for the quick feedback :)
Sure. I can also make theme files have the tokens listed with this structure so that everything is consistent.
Sorry I'm not sure I understood correctly. You mean keep the current mapping for old
Okay, will try to see about the webpack imports and fallback to just moving code if I struggle too much, good to know thanks.
Good to know, thanks. If I can allow myself to change the css variable names of legacyVariables right now though (like I understood in one [1]), is there not a risk of breaking external code using those variables then? Not talking about the |
Yes. In other words, anything in
You're correct that it can break external code referencing those variable names. The |
Okay well I'll leave current js name <> css names mappings as is. It's "just" one file having a couple big mappings after all, and it will prevent any surprises.
So actually I started changing things as you suggested but quickly stumbled upon cases where I started questioning what the categorization should be (like In the end I'd rather leave everything as is for now, it makes for a simpler switch (keep the same naming of previous colors+vars namings in the new tokens object). We could think about better names later maybe? I feel like this adds another layer of things to solve. |
Ok with me! |
c54bcde
to
f02621e
Compare
Ok, big update, hopefully you don't hate me too much 😬 I basically wrote a novel to explain everything in the latest commit but here is the gist of it since last time:
Questions/thoughts
|
In practice we've done this by trying not to make breaking changes to that file. For theming, @georgegevoian is the expert, but as I understood it the logical involvement that file had in theming was to inject a bunch of material sent to it from the surrounding app. Is it at all possible that you can move some of your changes into the material the surrounding app sends to |
Okay thanks to your help guys I think I managed to understand better and find a better solution for the grist-plugin-api thing :) Will keep you posted. |
Alright! Latest commit (that I'd fixup before merging) should make the grist-plugin-api issue go away. The final idea is:
To make this work I had to adapt the new format to the old way of attaching variables, so I added a few tests to make sure we don't make mistakes when writing new theme tokens. I think this is good to review @georgegevoian. One thing I noticed when searching for code sending "theme messages", as in the grist-plugin-api, is code in If any of you guys want to review @hexaltation @fflorent we can take some time in a call for context, I know this is a big chunk to swallow 🙈 |
0e6a992
to
dcf06c8
Compare
Deployed commit |
Deployed commit |
62d0a68
to
62d3047
Compare
Deployed commit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for slogging through this, @manuhabitela.
I've read through the changes and didn't spot any problems. (I admit I didn't every single property in GristLight.ts
and GristDark.ts
to make sure they still map to the same CSS value, but I haven't seen any differences so far during testing, and I don't expect there is anything serious to worry about anyway.)
I've done a good amount of testing already in the preview, but one thing I'd like to do is pull down the changes locally and do some additional side-by-side testing, particularly with some custom CSS changes in the mix. Since a lot of code got shuffled around, the main thing I'm checking for is that the behavior has remained consistent (so far, it has), and if it hasn't, whether it's something we can accept or mitigate.
Deployed commit |
Okay, I had a try at it in the last commit where I explain things again in the commit description.
What do you think? :) |
Deployed commit |
Deployed commit |
I think the approach of patching the mappings client-side is ok. Since the mappings have changed though, the same variables no longer apply to the same UI components. To maintain 100% compatibility, I think we'd need to expand every Checking for the "grist-custom" layer would be my preference, just to be safe. It's an unfortunate amount of gymnastics needed for the sake of supporting a feature that I suspect is not commonly used. We could just drop support for the old variables and communicate a link to the migration guide (once it's ready - before this lands). Let me check with the team to get opinions. |
Thanks for your quick feedback.
Not sure I'm following correctly on this one haha. From my understanding, all the new --grist-theme--prefixed variables that the --grist-color--prefixed variables now target match the previous values. Do you have an example in mind so that I understand better? |
|
New implementation of themes variable handling broke the support of custom.css files that don't use the new variables. This issue is mentioned by George and explained in details as to why in my comment: gristlabs#1340 (comment) This commit fixes that. Example: 1) Variables on load looks like this: ```css :root { --grist-color-dark: var(--grist-theme-body); --grist-theme-body: #262633; } ``` 2) I have an "old" custom.css file, it contains this: ```css :root { --grist-color-dark: #ff00ff !important; } ``` Without this commit, the UI breaks because the link to the new var is gone. The UI barely changed. I would need to manually update my custom.css file to change `--grist-theme-body` instead. With this commit, Grist appends this to the DOM automatically: ```css :root { ---grist-theme-body: var(--grist-color-dark) !important; } ``` We also add a warning in the console in order to encourage dev to update its custom.css file.
ae47ac2
to
4701f9d
Compare
Deployed commit |
New implementation of themes variable handling broke the support of custom.css files that don't use the new variables. This issue is mentioned by George and explained in details as to why in my comment: gristlabs#1340 (comment) This commit fixes that. Example: 1) Variables on load looks like this: ```css :root { --grist-color-dark: var(--grist-theme-body); --grist-theme-body: #262633; } ``` 2) I have an "old" custom.css file, it contains this: ```css :root { --grist-color-dark: #ff00ff !important; } ``` Without this commit, the UI breaks because the link to the new var is gone. The UI barely changed. I would need to manually update my custom.css file to change `--grist-theme-body` instead. With this commit, Grist appends this to the DOM automatically: ```css :root { ---grist-theme-body: var(--grist-color-dark) !important; } ``` We also add a warning in the console in order to encourage dev to update its custom.css file.
4701f9d
to
f698af9
Compare
Ok there was a bug in my "old custom.css file" fix, sorry about that.
Are you talking about changing the variable in a custom.css file? Can you try again with my updated branch? Just to be 100% clear: the idea of this PR is that we'd now use the new "grist-theme-*" tokens as the main source, like This is why I added the cssVars test: to make sure we don't update those values anymore while they still exist, as what they do now is only target the new tokens (those can change of course). I was thinking, I think I should update the example custom.css file with this PR, so that it uses the new theme variables? And explain in comments how to migrate easily maybe? Or explain it somewhere else? For example, the new custom.css could look like this:/*
If you are currently setting "--grist-color-" prefixed variables, you need to upgrade to the new css variable names.
See the mapping in the comments at the end of the file.
*/
@layer grist-custom {
:root {
/* logo */
--icon-GristLogo: url("ui-icons/Logo/GristLogo.svg") !important;
--grist-theme-logo-bg: #040404 !important;
--grist-theme-logo-size: 22px 22px !important;
/* colors */
--grist-theme-bg-secondary: #F7F7F7 !important;
--grist-theme-bg-tertiary: rgba(217,217,217,0.6) !important;
--grist-theme-decoration-secondary: #E8E8E8 !important;
--grist-theme-decoration: #D9D9D9 !important;
--grist-theme-white: #FFFFFF !important;
--grist-theme-body: #262633 !important;
--grist-theme-bg-emphasis: #262633 !important;
--grist-theme-secondary: #929299 !important;
--grist-theme-primary: #16B378 !important;
--grist-theme-primary-muted: #009058 !important;
--grist-theme-primary-dim: #007548 !important;
--grist-theme-primary-emphasis: #b1ffe2 !important;
--grist-theme-info-light: #87b2f9 !important;
--grist-theme-info: #3B82F6 !important;
--grist-theme-token-cursor: #16B378 !important;
--grist-theme-token-selection: rgba(22,179,120,0.15) !important;
--grist-theme-token-selection-opaque: #DCF4EB !important;
--grist-theme-token-selection-darker-opaque: #d6eee5 !important;
--grist-theme-token-cursor-inactive: #A2E1C9 !important;
--grist-theme-token-hover: #bfbfbf !important;
--grist-theme-error: #D0021B !important;
--grist-theme-warning-light: #F9AE41 !important;
--grist-theme-warning: #dd962c !important;
--grist-theme-backdrop: rgba(38,38,51,0.9) !important;
}
}
/*
Migration guide to new css variables.
If you are using "--grist-color-" prefixed variables, you need to upgrade to the new css variable names.
To help you migrate, listed below is the mapping of the old variables to the new ones:
--grist-color-light-grey: --grist-theme-bg-secondary
--grist-color-medium-grey: --grist-theme-bg-tertiary
--grist-color-medium-grey-opaque: --grist-theme-decoration-secondary
--grist-color-dark-grey: --grist-theme-decoration
--grist-color-light: --grist-theme-white
--grist-color-dark: --grist-theme-body
--grist-color-dark-bg: --grist-theme-bg-emphasis
--grist-color-slate: --grist-theme-secondary
--grist-color-lighter-green: --grist-theme-primary-emphasis
--grist-color-light-green: --grist-theme-primary
--grist-color-dark-green: --grist-theme-primary-muted
--grist-color-darker-green: --grist-theme-primary-dim
--grist-color-lighter-blue: --grist-theme-info-light
--grist-color-light-blue: --grist-theme-info
--grist-color-orange: --grist-theme-warning-light
--grist-color-cursor: --grist-theme-token-cursor
--grist-color-selection: --grist-theme-token-selection
--grist-color-selection-opaque: --grist-theme-token-selection-opaque
--grist-color-selection-darker-opaque: --grist-theme-token-selection-darker-opaque
--grist-color-inactive-cursor: --grist-theme-token-cursor-inactive
--grist-color-hover: --grist-theme-token-hover
--grist-color-error: --grist-theme-error
--grist-color-warning: --grist-theme-warning-light
--grist-color-warning-bg: --grist-theme-warning
--grist-color-backdrop: --grist-theme-backdrop
--grist-font-family: --grist-theme-font-family
--grist-font-family-data: --grist-theme-font-family-data
--grist-xx-font-size: --grist-theme-xx-small-font-size
--grist-x-small-font-size: --grist-theme-x-small-font-size
--grist-small-font-size: --grist-theme-small-font-size
--grist-medium-font-size: --grist-theme-medium-font-size
--grist-intro-font-size: --grist-theme-intro-font-size
--grist-large-font-size: --grist-theme-large-font-size
--grist-x-large-font-size: --grist-theme-x-large-font-size
--grist-xx-large-font-size: --grist-theme-xx-large-font-size
--grist-xxx-large-font-size: --grist-theme-xxx-large-font-size
--grist-big-control-font-size: --grist-theme-big-control-font-size
--grist-header-control-font-size: --grist-theme-header-control-font-size
--grist-big-text-weight: --grist-theme-big-control-text-weight
--grist-header-text-weight: --grist-theme-header-control-text-weight
--grist-border-radius: --grist-theme-control-border-radius
--grist-logo-bg: --grist-theme-logo-bg
--grist-logo-size: --grist-theme-logo-size
These variables are not used anymore and can be safely removed from your custom css:
--grist-color-dark-text
--grist-primary-bg
--grist-primary-fg
--grist-primary-fg-hover
--grist-control-font-size
--grist-small-control-font-size
--grist-label-text-size
--grist-label-text-bg
--grist-label-active-bg
--grist-normal-margin
--grist-normal-padding
--grist-tight-padding
--grist-loose-padding
--grist-control-border
--grist-toast-bg
*/ |
Working nicely for me now - thanks!
Yes, I'm following. One bit of uneasiness I had with the changes was breaking existing |
One thing I'd like to understand (and apologies if I missed an answer earlier in the discussions) is how custom themes will work, of the kind that your team @manuhabitela is using? Will it be a file with definitions of tokens like |
@dsagal So, to recap my thoughts and previous conversations we had about this: About built-in themes
About self-hosters customizationFor actual custom themes where we want to change primary colors when self-hosting like us, I think still doing it through the custom.css file is enough. Compared to current way of doing it, this PR makes it clearer what to change because variables are now color agnostic (no more One thing we would need is to allow the user to select a theme even when custom.css file is enabled. For now, it isn't. This means, a user couldn't select the fully accessible theme if I setup a custom.css file. Not acceptable for me (if Grist does a correct job of being more accessible, we would not want this to be disabled when self-hosting). I talked about this in #1396. For me, just adding a class or data attribute on the html tag, with the current theme name, would be enough. It would allow to have different customization depending on current user selected theme. |
f698af9
to
11c4634
Compare
New implementation of themes variable handling broke the support of custom.css files that don't use the new variables. This issue is mentioned by George and explained in details as to why in my comment: gristlabs#1340 (comment) This commit fixes that. Example: 1) Variables on load looks like this: ```css :root { --grist-color-dark: var(--grist-theme-body); --grist-theme-body: #262633; } ``` 2) I have an "old" custom.css file, it contains this: ```css :root { --grist-color-dark: #ff00ff !important; } ``` Without this commit, the UI breaks because the link to the new var is gone. The UI barely changed. I would need to manually update my custom.css file to change `--grist-theme-body` instead. With this commit, Grist appends this to the DOM automatically: ```css :root { ---grist-theme-body: var(--grist-color-dark) !important; } ``` We also add a warning in the console in order to encourage dev to update its custom.css file.
Alright, I updated the custom.css file. Let me know if this all ok for you, I'll then squash some commits that don't really need to be set apart. Thanks! |
Thank you. Looking over the most recent changes now... |
Deployed commit |
Deployed commit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks @manuhabitela.
This big commit reworks the way css variables/theme variables are written and loaded in the app. Until now themes described each component variable. This made creating a theme difficult: we had to override a bunch of hard-coded component variables (around 435 total) if we wanted to change one color shade used globally. Some CSS variables were not defined in a theme, but as theme-agnostic tokens, in cssVars.ts. But those tokens applied values related to the light theme, and couldn't be overriden easily in the dark theme. Also, the theme-related css variables were defined in cssVars, with default values, matching the ones in the light theme. This lead to some confusion because sometimes the fallback value described in cssVar wasn't in sync with the actual value in theme. The main idea of this commit is to make it easier to write theme and update theme files: - a theme file now describes around 20 main design tokens, defining the main text color, backgrounds, primary colors, etc. The hundreds of variables it defined previously are now inside a `components` key to explicitely show they are more specific. Most of those variables target one of the main design tokens to help with updating the styles globally. - a new Base.ts theme file was added, that the actual theme files use as a… base. It contains the design tokens we assume won't change 99% of the time. - to prevent too much context switching in our dev heads, now theme tokens are defined with js object keys styled names, instead of css var names (ie bgSecondary instead of 'bg-secondary'). - a new theme `tokens` object exposes all the CSS variables related to the current theme, that the codebase can consume. They are meant to be used just like cssVars `colors` and ` vars` are used today, and actually the idea would be to eventually remove those cssVars exports so that `tokens` is the only one used. Some `vars` were not migrated to a matching `token` because they were not used in the codebase, but we kept it to not break custom css/custom widget that may use the variables. - a new theme `components` object exposes all the previous cssVars `theme` variables. The current cssVars `theme` object just consumes this new object, but we could also imagine removing it altogether in the future. - for all of this to work, now a theme is always loaded, even when custom CSS is enabled, or when theme choice is actually unsupported (forms). In these latter cases, the light theme is loaded. Limitations: - I tried to have more "theme-agnostic" variable names for the gray shades and the primary color (green). The tricky part is, in the light theme there are way less dark-shades and green shades used than in the dark theme (colors are not a 1:1 match) so it's a bit hard to come up with generic, theme-agnostic tokens. This can certainly be improved but my guess is now is a good middle ground. Before/after stats for the theme "components" variables: - a theme had around 435 variables set before, all hard-coded strings - now a Base theme, common to dark and light, defines around 290 values, and around 10 of those are hard-coded, the other target the more generic tokens, making it easier to update - a specific theme (dark or light) defines around 150 variables. In each theme, around 60 of those are hard-coded. That means while there is still a big room for improvement, themes should be noticeably easier to maintain, as most values come from generic tokens. Second idea of this commit (it should be in its own commit but it was too hard to dismantle afterwards, sorry…) is the introduction of css at-layers rules. "grist-base", "grist-theme" and "grist-custom" css layers are used in order to easily handle priority of css variables. With the layers we make sure custom.css file variables take precedence over theme variables, themselves taking precedence over base, theme agnostic css variables. One limitation was we can't easily update how the variables are appended in the DOM. This is because grist-plugin-api, the file making the link between the app code and widget iframes code, can't be updated easily with breaking changes (we'd rather be somewhat limited in the way we code rather than dealing with breaking changes). And one of its jobs is to load the css variables. So while themes are now described with "js names" objects, they still need to be given to the theme loading system with "css variable names". This is why the 'convertThemeKeysToCssVars' helper was made.
New implementation of themes variable handling (previous commit) broke the support of custom.css files that don't use the new variables. This issue is mentioned by George and explained in details as to why in my comment: gristlabs#1340 (comment) This commit fixes that. Example custom.css file was also updated to use the new variables, and explain how to migrate. Example: 1) Variables on load looks like this (in grist app): ```css :root { --grist-color-dark: var(--grist-theme-body); --grist-theme-body: #262633; } ``` 2) I have an "old" custom.css file (as a selfhoster), it contains this: ```css :root { --grist-color-dark: #ff00ff !important; } ``` Without this commit, the UI breaks because the link to the new var is gone. I would need to manually update my custom.css file to change `--grist-theme-body` instead. With this commit, Grist appends this to the DOM automatically: ```css :root { ---grist-theme-body: var(--grist-color-dark) !important; } ``` It also adds a warning in the console in order to encourage devs to update their custom.css file.
e23b5cc
to
6509874
Compare
Thank you very much for taking the time for this George. I rebased to have cleaner commits. |
Deployed commit |
Context
Here is an example of implementation following discussions in #1295 (up until George's comment).
Fixes #1295.
Proposed solution
Everything is detailed in comments and commit messages, here is a summary of it:
cssVars.colors
andcssVars.vars
, have one big unique object that holds all the design tokens (cssVars.designTokens
), that can all be overriden by a theme. Contrary to before where default colors could not be changed. The idea would be that the new object replaces the 2 others.cssVars.theme
, make the specific component variables target the new design tokens. In the end, in each Theme, specific component variables can be removed, and we can decide to define only the more global design token values (see GristDark).Questions that popped up:
cssVars
being used for the undefined variables. I'm not sure this is a great idea as it could lead to oversights when implementing new themes or updating current ones. I'm not sure it's actually an issue either. As we can easily create a new Theme by extending an existing one anyway. What do you think?Related issues
#1295
Has this been tested?
ping @dsagal @georgegevoian