Re: [csswg-drafts] Proposal: CSS Variable Groups (as a solution to several design systems pain points) (#9992)

> I suspect we don't want to allow that, just because that means we have to be a lot more literal with the nested bits. Like, we'd have to allow `--foo*: {s: ...; }`, which means we're closing off the ability to do any of the base/etc stuff. Whereas if we require a dash, we can require it on _both_ ends, like `--foo-* { --bar: ...; }` and keep the namespaces nicely separated. (Also, requiring a `--` on the nested bits keeps the parsing working nice even when the bits are just numbers, like `--gray-* { --500: ...; }` works right now while `--gray-* { 500: ...; }` doesn't.)

I actually agree with this. It's a little uglier, but I think the lack of ambiguity is worth it.

> > Did you see the entire table of popular design systems that I included in my proposal?
> 
> Yes, but that's not what I was asking for. I'd want to see _worked-out examples_ of how such design systems would use this, and how it would make their usage of their own design systems easier and more reliable. Ideally every reader of the proposal doesn't have to do the working out themselves. ^_^

I’m still not super sure what you’re asking. The variables they define today are right there, but they're obviously hardcoded today.

> Can you give an example?

I gave several in #10034. Though this kind of approach does restrict the interpolation tweaking to the design system author, but perhaps that's ok? That said, if we could find a way to allow tweaking from the outside, even better.

> My argument isn't that we should treat data structures and functions as equivalent. It's that your proposal spans _the full gamut_ from "obvious data structure" (variable groups) to "obvious function" (variables with continuous variation), and we should be careful about how we handle this.

Can't disagree with being careful! :) But that applies to everything we do :) 

> CSS is already going to grow functions in some way. Do we want two distinct function syntaxes - one defining globally using at-rules, and one defining locally using properties? If so, how much can they differ in power/expressiveness? How much flexibility do we even _have_ in declaration syntax? How much will this cost us in terms of future syntax flexibility?

I don't see this as two function syntaxes, in the same way that JS accessors and proxies are not alternative function syntaxes. But these are all questions we *should* figure out.

> Currently, I'm _very_ hesitant in trying to define a separate, second function syntax, and _extra_ hesitant about doing so _within the bounds of declaration syntax_. I would need an _extremely_ compelling argument that it's both necessary, and the only way to do so, before I'd be willing to go for it. (I'm _super extra_ hesitant about doing any of this _before we have even actually defined the first type of functions_.)

I think it may be worth exploring implementability (especially compared to functions). What if shipping dynamic properties *first* could cover enough use cases that we can then take our time fleshing out functions?

But I do think that the use cases are distinct, although there is certainly overlap. In some ways, this is like saying we shouldn't work on `if()` before `@if`.
Dynamic properties are more limited in terms of arguments, but also have full access to context, are defined inline so are easier to debug, and (the biggest benefit IMO) cascade and inherit like normal properties.

> Because we still have to answer the question: how much _must_ we do? Can we do a fairly small feature (variable groups) that solves 90% of the problem, and just skip the last 10%, thus avoiding having to define a massive new feature (declaration-syntax cascading functions masquerading as custom properties)? If that 10% _does_ need to be solved, are there other ways to do it that aren't as heavyweight of a feature?

I think the most pervasive pain point is the mass property renaming / passing around. So while I'm not sure if it would be 90% or 80% or 70%, I think addressing that is the bigger priority.

> In the absence of answers to those questions (reasonable, because this is a very early exploration into the space!), I'm trying to push on the boundaries a bit. How much _can_ we solve using existing (or planned) features instead? If we do have to introduce new features, what other possibilities exist that might be lighterweight in syntax and/or functionality?

I think decomposing the problem into a bunch of lower level features that can ship independently is often a good path (and what I was trying to do with the alternative decomposed design).

> Generally speaking, I disagree with this! What you expose _and how you expose it_ are important considerations in API design. If, in JS, I hand my user an object and they're expected to access properties on it, that _implies_ that there's a finite, predetermined number of values I can access. If I hand them a function that takes a continuously-varying argument, that _implies_ that there's a continuously-varying output value. If I swap either of these, I'm somewhat violating expectations, and better have a really good reason for doing so.

While I agree that it depends on the case, and in many cases you definitely want to expose that. But there are also many legit cases where that *is* an implementation detail, which is why there are entire programming language features *designed* to mask exactly that (e.g. accessors and proxies in JS).

Also keep in mind that the tokens the design system desires to expose may still be finite — it's the _definition_ that is continuous. In the same way that typeface designers may design a typeface by interpolating between faces, then export a finite number of faces.

> As an example of a different possible approach, perhaps the use-case here can solved in a more narrowly-targeted fashion. For example, maybe we can define a "value ramp" (a few types - numeric, color, others) that represents a range of interpolated values, can be passed around in custom properties, queried for particular values on the ramp, and extended by others. 
> [snip]

This is fascinating, I posted #10034 last night before reading this, and it looks like we’ve been thinking along very similar lines. I agree the piecewise interpolation stuff was not a good fit for this proposal and I have now removed it.

Some comments as I read through your proposal:

First, I see the benefits an `@`-rule would bring, but I don't think they outweigh the cons:
-  I would *really* like to avoid anything that has to be tree-scoped, like an `@`-rule. 
- I think it should be a requirement that it should be possible to pass these scales around with variables, so an `@`-rule is not conducive to that either.
- Defining an `@`-rule feels much more heavyweight and forces you to find a unique id. E.g. I've seen authors go to some pretty wild lengths to use transitions instead of animations because of this this.

Wrt overridding the interpolation, there are two use cases here:
- The design system _author_ doing the overriding, e.g. to make darker yellows orangish, to spread out lighter tints etc. I think that's the bigger use case around overriding, and an inline function caters to that just fine, as it's defined by the design system author.
- The design system _user_ doing the overriding, e.g. the red 900 tint is too dark for my liking, I’ll make it a bit lighter.

I don't see why we'd bake the specific tint levels into the ramp. I think one of the advantages of a rank primitive is to abstract the naming scheme away, and `progress()` makes it easy to apply it externally. I think that's more of a nice to have, and not that important to address. It also seems to be interoducing a new alternative color stop syntax, when we already have a color stop syntax for exactly this very thing!

I *really* like the idea of generating ramps from existing ramps. I think that is very powerful, and does this without having to resort to an `@`-rule. Amazing!

> This could sprout more abilities, too, like the ability to round the input to some precision (only every 100, or 50, or whatever), define whether it extends past the first/last stops, etc.

Yes!

> This sort of approach would also suffer from the "everyone has to use this method" problem you outline

Not necessarily. Design systems could still generate aliases that call `color-ramp()` internally. It's not ideal, but it's better than the current situation.

> Are all the design-system cases that want generative/continuous values doable with something like this? Are they all just sizes and colors, or other things that might similar fit into this framework? We'd need to see. Maybe they _would_ all just switch to this sort of value, were it provided, so you wouldn't need to worry about some using "a whole bunch of values stored in individual properties" that required a lot of manual renaming.

I see these as orthogonal problems. Providing better tools for ramps is useful in its own right (which is why I opened #10034). Like I said, I suspect design systems will still alias points along the ramps to variables — now if we give them a way to make this less repetitive in the future, even better!

-- 
GitHub Notification of comment by LeaVerou
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/9992#issuecomment-1981379030 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Wednesday, 6 March 2024 17:11:32 UTC