w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.47k stars 659 forks source link

[css-values] A more structured format for complex CSS values? #6519

Open proimage opened 3 years ago

proimage commented 3 years ago

https://www.w3.org/TR/css-values-4/ (I think...?)

CSS complexity is increasing, yet the ways we have to represent that complexity have not kept up. Currently, some CSS properties can have quite complex values, which in any "normal" programming language would be represented by an associative array (a.k.a. key-value array), but in CSS have to be represented in a simple string, with no "labels" for any of the sub-values.

As an example, look at how we specify a gradient for background-image:

background-image: [linear|radial|conic|pearShaped]-gradient([direction] [origin side], [color-stops]) [, rinse-and-repeat];

...and you all know how complex that innocent-looking [color-stops] can be. 🙄

Another example is box-shadow:

box-shadow: inset 1px 1px 1px 1px rgba(0, 0, 0, 0.2) [, rinse-and-repeat];

Both of these properties (and others like them) force authors to arrange "sub-values" in a single string, where the order of each sub-value is both vital and non-obvious.

Is it time to consider giving CSS a more structured data format (i.e. an associative array)?

@LeaVerou graciously pointed out that this "is not idiomatic to CSS and thus inconsistent with the rest of the language."

I respectfully disagree: is the entirety of CSS itself not already an associative array?

[selector]
{
    [key]: [value];
    [key]: [value];
    /* etc */
}

Is it impossible to consider allowing [value] to itself be an associative array?

/* fake code; don't try to use this */
box-shadow: {
    inset: true,
    x: 1px,
    y: 1px,
    blur: 1px,
    spread: 1px,
    color: rgba(0, 0, 0, 0.2)
};

Obviously considerably more verbose, but also considerably less ambiguous and more flexible.

I'm not proposing that this be applied or allowable for any of the simpler values (font-size: { size: 18px } would be a bit ridiculous... 🤣), but I do think there could be room to allow this type of value format for the properties with more complex values.

Crissov commented 3 years ago

Make that @shadow "my-shade" {…} foo {box-shadow: "my-shade"} (or the same with --my-shade) and you are on your path back to more conventional CSS.

proimage commented 3 years ago

If I'm understanding what you wrote correctly, that just seems to be offloading the issue elsewhere, and as you mentioned, is already accomplishable by using existing CSS variables:

--my-shade: inset 1px 1px 1px 1px rgba(0,0,0,0.2);

.element
{
    box-shadow: var(--my-shade);
}

That's not what I'm after though... I'm proposing a way to (optionally!) disambiguate those inset 1px 1px 1px 1px rgba(0,0,0,0.2) strings altogether.

Crissov commented 3 years ago

No, I was saying that some properties, like text-shadow and box-shadow, might benefit from new accompanying at-rules that would define named preset values for them. Inside such at-rules you would have descriptors (which look like properties) that have names similar to what you proposed.

Compare fonts and counters, for instance.

proimage commented 3 years ago

Ahh, apologies, I did indeed misunderstand you.

Is that the same convention that is used to define animations?

Crissov commented 3 years ago

Yes, @keyframes would be another example, although it uses progress-kinda-selector {property: value;} inside, while I suggested descriptor: value;.

tabatkins commented 3 years ago

So this sort of syntax has been brought up before. The big strike against is that if you can define the sub-parts of a property, you're already 90% of the way to just making the property a shorthand, with longhands like box-shadow-color, box-shadow-inset, etc. (And box-shadow and text-shadow have been requested to become shorthands for a long time, for precisely this reason.)

I don't know how much I agree with the reasoning - it's okay for single-value properties, but list-valued properties like box-shadow lose all the organizational benefit when you split them apart. It would be better if you could define each shadow in order, with named parts.

Using an at-rule is technically doable, but it means you have to define them out-of-band, and give them a name even if they're one-offs, and doing anything via the CSSOM APIs would get way more annoying since you have to manually construct the rules and insert them into a sheet, rather than just writing a string into .style. This is reasonable for things that are going to be reused and rarely modified, but not for arbitrary style values like a shadow.

Ultimately, I'm for something like this, but it would be quite an effort to define, since you'd probably want it to work for every existing shorthand in the language.

proimage commented 3 years ago

The main issue of CSS that I'm hoping this would help with is the non-obviousness in the order of certain property values.

There's a bit of a preventative measure here, in that it's solving a problem that perhaps isn't quite big enough to need a solution yet. But in peeking at a bit of what's being requested by devs, at a bit of what we're wanting CSS to be able to do in the future, it becomes apparent that certain properties are going to either have a very complex single-dimension (string) value definition (and complexity turns away new developers), or a more structured multiple-dimension (associative array) value definition.

Sometimes, the complexity will be able to be simplified by virtue of it only belonging to shorthand/combo properties, while the breakout longhand/individual properties make things fairly straightforward. However, in other cases (such as the requested enhancement to BG gradients linked above), the complexity isn't because it's a shorthand, but because it's just freaking complex. :p

There's only so much data we can cram into a single-line string before it becomes unwieldy to maintain. ¯\_(ツ)_/¯