-
Notifications
You must be signed in to change notification settings - Fork 689
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
[css-mixins] Improve ergonomics of using
by allowing wildcards
#10954
Comments
I'm slightly against this. The use-case is reasonable, but I think we should solve it more directly. Basically, a function knows exactly what variables it is going to use. There's no need to import every single color; just write the ones you're actually using in your The problem is that a function doesn't know what theming variables its own called functions will want to use, and so pulling in the entire theme, as in your example, makes sense. This means it would probably end up being common practice to just write We do want to make sure that an outer function can override a theming variable for the functions it calls (and the functions they call, etc, if they're not further overriden). This sort of theming control is important. Maybe Here's an example: @function --outer() {
--v1: 10;
result: --inner();
}
@function --inner() using (--v1: 100, --v2: 200) {
result: calc(var(--v1) + var(--v2));
}
.foo {
--v1: 1;
--v2: 2;
z-index: --outer();
} Under the current specced behavior, the z-index is 210 - Under the alternative I describe above, the z-index is 12 - @andruud, any thoughts? |
I agree that we should, and there are several open issues to do so. However, even if we solve it for the common case, I think there will always be use cases where you want to pull in multiple properties with a shared prefix.
Or we define that the using lists of each function are separate, a function calling another function doesn't need to specify a superset of using for the child call to work. It’s harder to implement, but much better ergonomics IMO.
Agreed. One solution could be to simply not allow that, specify that you need at least one character.
I had assumed a function’s parameters shadow any custom properties in
I like that. |
Wouldn't CSS Variable Groups (#9992) be a viable solution? |
I'm not sure we should have
That problem exists because we have
|
Agreed with @andruud that What if variable references that the function is not able to resolve are propagated and resolve at the point of calling? Would something like that work? |
Both of these mean that functions can have arguments that one calling context is allowed to control (the element itself), but all other contexts can't. If you write a function that calls other functions, the "global" variables they're grabbing simply bypass you entirely. Neither of them fix the "dynamic scoping" aspect of the concept, they just make that scoping even less controllable. I'm not sure why we'd want that. The point of Like, check out my Bikeshed usage of Looked at another way, this is just carrying the custom property inheritance behavior from elements into functions. We already recognize that being able to override a custom property on an entire subtree by changing it on the common ancestor is useful; it would be quite bad if every single element in the subtree had to manually indicate it was inheriting all the custom properties its descendants were using. The point of adding I am opposed to (a) not exposing element variables at all, so arg lists have to be enormous and coordinated, or (b) exposing specifically element variables, in a way that can't be overridden at the call site, as it means inlining a function can change its behavior. Maintaining beta reduction is important, I think. |
If I'm understanding this correctly, this is what I just suggested with "remove |
I think so, yes. I think that makes a lot more sense. Q: It would be nice if there was a JS API to add pure computational functions to CSS down the line. Would this preclude such an API? |
Not necessarily. It just means that, rather than declaring which vars you want, and being given an object containing just them, we'd have to give you an object containing every visible var. |
I like the proposal to remove |
That is "full" dynamic scoping, then. If that's indeed what we want, well OK. But should mention a good point (made elsewhere) by @mgiuca, that dynamic scoping breeds chaos for closures/higher-order functions. (Imagine passing a function around in a value and have that behave differently depending on where it's invoked.) So I think a resolution to do dynamic scoping, is also a resolution to never do closures or similar in CSS. (That might be OK.) |
That makes sense. I wonder if people have use-cases in mind for that. |
Dynamic scoping is how CSS variables generally behave though, so it seems like that ship has sailed? |
I don't think what we decide here has any relevance for closures later, actually. Functions as we're defining them simply do not have a meaningful lexical scope - they're defined at the stylesheet level, which doesn't contain anything except global state; importantly it doesn't contain any custom properties. So all non-global references must be either lexical from arguments, or dynamic from the calling environment (the element the value is being set on); that's the only choice. If we ever introduced closures (property-level function definitions, rather than global), we could, if we chose, distinguish between lexical variables (drawn from the element the function was defined on) and dynamic (drawn from the element the function is executed on). I imagine we'd reuse this |
Instead of using arg() to reach arguments, var() is sensitive to the function context, letting arguments (and in the future locals) shadow "globals" from the element. Note that "globals" (i.e. custom properties from the element) being automatically available within functions is not yet specified, but CSSWG discussions definitely lean towards that outcome [1]. [1] w3c/csswg-drafts#10954 Bug: 325504770, 344608183 Change-Id: I4efa3d1149120538361c94c39f0dbdc268f73fb5 Binary-Size: crbug.com/344608183, maybe. Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6157722 Commit-Queue: Anders Hartvoll Ruud <[email protected]> Reviewed-by: Steinar H Gunderson <[email protected]> Cr-Commit-Position: refs/heads/main@{#1405946}
The CSS Working Group just discussed
The full IRC log of that discussion<kbabbitt> TabAtkins: earlier I had implied that the interior of a fuinction can see custom props on element<kbabbitt> ... questio is how should that be possible to what degree to make it automatic <kbabbitt> ...current spec makes it not automatic at all <kbabbitt> ... if you want to see custom props inside function you have to declare <kbabbitt> ... then they become local varis inside function body <kbabbitt> ... lea pointed out that doesn't work well in design systems with 100+ custom props <kbabbitt> ... functions called by other functions also need to declare <kbabbitt> ... lea's suggestion at first was to have some wildcarding syntax <kbabbitt> ... but what I came around to instead was to remove that syntax entirely form fcuntion defintion <kbabbitt> ... make all custom props visible inside custom function <kizu> q+ <kbabbitt> ... arguments shadow if they have same name <emilio> q+ <lea> strong +1 to TabAtkins' proposed behavior <kbabbitt> ... my preference is to make all custom props behave similarly <kbabbitt> ...nested variables visible in calling function in the same way <kbabbitt> ... so you ca override when necessary but otherwise don't have to think about it <lea> Way more flexible than the current syntax OR the wildcard syntax I proposed <miriam> this matches my original proposal, and I like it :) <kbabbitt> lea: the using feature is a wart in general, glad to be rid of it <kbabbitt> ... makes a lot more use cases possible <astearns> ack kizu <astearns> q+ <kbabbitt> kizu: no objection, huge +1 <kbabbitt> ... only thing I want to mention is we could still keep using syntax if we want to allow renaming <kbabbitt> ... variables from outer scope <lea> q? <kbabbitt> ... if we want to use same names in inner scope or as argument names <kbabbitt> .... could using foo to define outer foo as something else <lea> q+ to say this sounds like a L2 feature, and something we need more data on <kbabbitt> ... other than that, having all custom props from outside available at all times would be much more useful <kbabbitt> TabAtkins: already possible today if you declare a local var and set its value to var to pull from <kbabbitt> ... probably don't need additional syntax for that <kbabbitt> ... theoretically there for the future <astearns> ack emilio <kbabbitt> emilio: was wondering use case for this originally? <kbabbitt> TabAtkins: I wanted to be careful about encapsulation <kbabbitt> ... so you don't accidentally grab unintended variables <kbabbitt> emilio: seems fine to allow it <kbabbitt> ... from impl perspective, easy to compute set of variables if needed <ChrisL> q+ <kbabbitt> ... manual list seems fine <kbabbitt> TabAtkins: other aspect was, assuming we'll do a JS backed custom fn as well <kbabbitt> ... at some point <kbabbitt> ... using syntax made it clear exactly what would be passed to JS context <kbabbitt> .. instead we'll gether all custom props, probably not a big deal <lea> q+ to reply to emilio: do we even need to compute them? We can just propagate these `var()`s to the result and have the function return an expression that contains them <kbabbitt> ...certainly easier to avoid massive repetition in JS than CSS <kbabbitt> astearns: if it was a good optimization, you could take a look at what props were used and subset things when passing it over js boundary <kbabbitt> TabAtkins: aliasing is a hard problem <kbabbitt> astearns: to clarify: what happens when a custom prop is not defined? <kbabbitt> ... and function is using it? <kbabbitt> TabAtkins: it's just a var reference <kbabbitt> ... you use the var syntax, same as anything else <astearns> ack astearns <astearns> ack lea <Zakim> lea, you wanted to say this sounds like a L2 feature, and something we need more data on and to reply to emilio: do we even need to compute them? We can just propagate these <Zakim> ... `var()`s to the result and have the function return an expression that contains them <kbabbitt> lea: re: kizu - yes I can see potentially things to adjust how scoping works <kbabbitt> ... possibly opposite of using and excluding vars <kbabbitt> ... sound like level 2 features <weinig> q+ <kbabbitt> ... don't want to expand scope right now, keep it lean so we can ship soon <kbabbitt> ... re: emilio - do we actually need to compute the union of props being used? <kbabbitt> ... just propagate var references instead? <kbabbitt> ... if using vars from outside, instead of fn returning single value, could return an expression that includes var references <kbabbitt> ... then they get resolved when used <kbabbitt> emilio: didn't mean to imply they needed to be computed <kbabbitt> ... looking at names fns are using, can see the names while parsing <kbabbitt> ... join them when substituting <kbabbitt> ...if we need for some super fancy optimization we could get that list <kbabbitt> lea: awesome <astearns> acl ChrisL <astearns> ack ChrisL <kbabbitt> ChrisL: I understand desire from CS standpoint to want to do data hiding <kbabbitt> ... but in this case passing things it doesn't actually care bout but 8 level deep does care <kbabbitt> ... passing them through produces less footguns <astearns> ack weinig <kbabbitt> weinig: to clarify.... using the programming language jargon, is what's provided here dynamic scoping? <kbabbitt> TabAtkins: yes <kbabbitt> weinig: where you call the function, element or scope there defines additional ? <kbabbitt> TabAtkins: variable definition doesn't have meaningful scope <kbabbitt> ...carrying on existing dynamic scoping of inheritance that custom prtops rely on <kbabbitt> Proposed: Kill using with fire <TabAtkins> s/meaningful scope/meaningful lexical scope/ <kbabbitt> RESOLVED: Kill using with fire |
Resolves #10954. Co-authored-by: Anders Hartvoll Ruud <[email protected]>
using
is a great first step, but in practice if you actually need access to design system variablesusing
will get extremely unwieldy.For example, this is what it would look like if you want access to Open Props colors:
It gets even worse if you want access to other tokens too.
With wildcards, it would become the much more manageable:
And for many other design systems that use some kind of prefix for all colors (e.g.
--wa-color-*
), it would be even simpler.It would even allow passing all custom properties, by simply doing
--*
.The text was updated successfully, but these errors were encountered: