-
-
Notifications
You must be signed in to change notification settings - Fork 82
feat: CSS variable tracking #136
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
Conversation
|
|
||
| ## Drawbacks | ||
|
|
||
| The primary drawback of this approach is that it only tracks custom properties within a single file. It cannot resolve custom properties that are defined in other files or via mechanisms like inline styles on HTML elements. For the initial implementation, this is considered an acceptable tradeoff. |
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.
I'm glad you called this out. This drawback may not be enough to limit this proposal, since it does have some really nice features, but I think it will severely limit the ability to achieve these two goals
- Detecting references to undefined custom properties.
- Future: Detecting unused custom properties.
I'm not sure how that will be possible without being able to span multiple files.
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.
All of this logic is already implemented in the no-invalid-properties rule and it works pretty well. We call out the limitations. The alternative of doing nothing means any property value containing a variable can't be validated or otherwise inspected in any way, which I don't think is acceptable.
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 that context. Maybe I've worked in large corporate codebases for too long, and this is biasing my perspective, but I guess I can't really see a case in any project that I've worked on, where a rule with that limitation could be used. In my experience, anything even reasonably sized will have custom props defined in a different place than where they're used. Whether it be a large design system with design tokens, or just a common set of reusable props that an app uses across its codebase (or both in many cases).
In our case, specifically, our design system team also publishes an eslint plugin that enforces use of their design tokens. e.g. if you're defining a color with a raw hex value, flag that as a violation, since that should be using a custom prop from their catalog of tokens.
So yeah, I'm not sure my feedback is actionable for this proposal. Just sharing my perspective and somewhat questioning how realistic the larger goal is (in most use cases), when this limitation is in place.
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.
I agree with @michaelfaith. Rules like no-invalid-properties as written today only work on a very small scale. Most -I would wager nearly all- modern codebases split their styles across many files. The (roughly) simplest common approach is to have variables declared in one file and then used in other files. Even that approach completely breaks the current & proposed variable tracking logic.
Furthermore, even if the tracker factored in multiple CSS files at a time, it would still suffer from not knowing which variables are used where. It's also common for projects to define variables on something other than :root, such as a specific page container. Any case where variables are added/changed in specific element scopes would be broken too.
Quick reproductions here: https://github.com/JoshuaKGoldberg/repros/tree/eslint-css-variable-tracking
Given the constraints of ESLint (in particular that cross-file information is out of scope for this discussion), my opinion is that:
- Any CSS variables tracking done by ESLint or its rules is not usable for the majority of ESLint's prospective users
- Because of how broken variable tracking is in the common case, it would only be reasonable for depending on it to be an opt-in choice of users
- That choice is such a rare one, it would not be worth the technical investment to build it into eslint/css and/or its rules
I.e. I think no-invalid-properties and any other rules depending on variable tracking should be changed to no longer do so. Because:
any property value containing a variable can't be validated or otherwise inspected in any way
I think this is actually the reality today.
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.
I'm sorry, I just don't subscribe to the belief that if we can't get it perfect, we should do nothing. At a minimum it works for some cases, and in the best case, this is a solid foundation upon which we can build.
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.
Yes I do, as I think your perspective on real-world use cases differs from mine.
When working on large-scale projects, it's easy to forget that there are a lot of small-scale projects made by hobbyists or self-taught folks who don't necessarily adhere to best practices. (Anecdote: On my personal blog I use a single stylesheet -- I doubt I'm alone in this.) For those folks, this capability will be huge.
For folks who do work on large-scale projects where custom properties are located in an extra file, there's zero downside to having this functionality. They just don't get the extra checks, that's it.
I don't think there's a valid argument to be made that just because we can't support all use cases means we can't support any use cases.
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.
👍 I can understand that perspective, it makes sense. The only counterpoint I'd give is:
zero downside ... They just don't get the extra checks, that's it.
As currently implemented in eslint/css and proposed in this RFC, I don't believe that's the case? no-invalid-properties falsely reports in many cases. That's what I'm trying to avoid: setting up rules to falsely report on a large percentage of users.
To avoid that, I'd like to suggest:
- This RFC explicitly call out the kinds of file setups that do and do not work well with its proposed tracking
- Starting a precedent of rules that rely on variable tracking (or other, similarly nuanced "works in only some setups" behavior) do something to make sure projects not set up for them don't enable them. Maybe: documentation? Different config(s) and/or shared settings indicating project architecture style?
Does that seem reasonable to you? I can file an issue for the latter if so.
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.
As currently implemented in eslint/css and proposed in this RFC, I don't believe that's the case?
no-invalid-propertiesfalsely reports in many cases. That's what I'm trying to avoid: setting up rules to falsely report on a large percentage of users.
The first case, the false positive, is solved with ignoreUnknownVariables, so I don't see that as an issue. We have the escape hatch there so people can use it.
The second case is not a false negative. As I've explained elsewhere, for the purposes of validation, we only need to know the type of the property. The likelihood that --colorForeground will ever be anything other than a type compatible with <color> is vanishingly small. In this example, because --colorForeground is a color and it's used inside of a color property, it should be considered valid. It really doesn't matter if we can tell the exact value of --colorForeground in any given rule, which is functionally impossible without having all of the CSS and all of the HTML to evaluate together -- it just matters that we know the type of data that --colorForeground represents, and we do.
- This RFC explicitly call out the kinds of file setups that do and do not work well with its proposed tracking
I feel like the downsides section already does this.
- Starting a precedent of rules that rely on variable tracking (or other, similarly nuanced "works in only some setups" behavior) do something to make sure projects not set up for them don't enable them. Maybe: documentation? Different config(s) and/or shared settings indicating project architecture style?
We did have a "Limitations" section on the no-invalid-properties docs page, but it looks like that was accidentally removed with the inclusion of ignoreUnknownVariables. I think it's reasonable to add that to the docs of rules that use variable tracking.
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.
That makes sense, thanks. I know this thread is getting long but I have one more suggestion: would it make sense to build the concept of "this project (can/can't) rely on variable tracking" into this RFC?
Thinking out loud: right now, multi-file projects need to manually turn off any rule features that rely on variable options. Right now that's just the 1-2 rules but presumably it'll grow over time. Each rule would need to add a user-facing option to turn off variable tracking, and users would need to manually configure each of those options.
A couple of ways come to mind to be able to configure in one place:
- Provide config(s) like
css/recommended-multi-file - Provide a shared setting like
css.variableTracking.multiFile- For any rules with an option like
ignoreUnknownVariables, they could default that option to the shared setting's value
- For any rules with an option like
I think the shared setting approach is more idiomatic. Adding many more configs gets messy, especially if other cross-rule concerns like this one pop up.
I'm not sure that the variable tracker itself needs to know about the shared setting. It might make more sense just to have the rules react to the presence of the shared setting. But since we're discussing the tracker here, I wanted to bring it up now.
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.
I don't think it makes sense to design a solution for a problem we don't have yet. When people start filing issues about such things, we can take a look and see what (if anything) makes sense to address it.
| } | ||
| ``` | ||
|
|
||
| A new public method, `getVariableValue(node)`, will be added to `CSSSourceCode`. This method will take a `var()` `Function` node as an argument and return the computed value of the custom property (which is currently always a `Raw` node). It will do this by searching for the last declaration of the custom property that appears before the given `Function` node in the source code. This also leaves open the possibility that we could change how this value is calculated to be more accurate in the future. |
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.
From what I understand, a new method getVariableValue(node) that returns a single Raw node will be good enough to support the functionality currently provided by no-invalid-properties, since that rule only considers the value of the last declaration. If the intent is to make the value calculation more accurate in the future, I think we should consider the possibility that a variable could assume different values regardless of the order of declarations, and in that case returning a single node may not be sufficient. Here is a simple example that illustrates the point:
:root .dark-theme {
--backgound: #000;
}
:root :not(.dark-theme) {
--backgound: #fff;
}
div {
background-color: var(--backgound);
}In the above code, the variable --background could take either of the values specified in the declarations, regardless of the order, because the values have different selectors. There is no single effective value for the variable in the context of the CSS file. For this use case it would be better if getVariableValue returned all applicable value nodes, rather than the last one.
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.
I also think it should return all applicable value nodes, or maybe both all and the last before (e.g, { all: Raw[], lastBefore: Raw }, so that rule authors can decide whether the rule should consider all possible values or just one that might be the most likely.
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.
So then maybe two methods? getClosestVariableValue() and getVariableValues()?
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.
So then maybe two methods?
getClosestVariableValue()andgetVariableValues()?
Sounds good to me.
|
I realized we should also take into account |
Co-authored-by: Francesco Trotta <[email protected]>
|
Is there any reason this can't be moved to final commenting? |
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.
Sorry for the delay, just trying to split my time across tasks. This is looking good already, I only left a couple of comments.
Co-authored-by: Francesco Trotta <[email protected]>
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.
Looks good to me overall, just one suggestion.
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.
Just a few fixes, then LGTM.
Co-authored-by: Milos Djermanovic <[email protected]>
Co-authored-by: Milos Djermanovic <[email protected]>
Co-authored-by: Milos Djermanovic <[email protected]>
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!
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. Moving to final commenting.
|
I'm going to merge this now that the final commenting time is over. |
Summary
This RFC proposes to add capabilities to
CSSSourceCodeto track CSS custom properties (also known as CSS variables). This includes identifying where custom properties are defined and where they are used, as well as providing a way to retrieve the value of a custom property at a specific location in the code.Related Issues
refs eslint/css#160