w3c / csswg-drafts

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

[css-color] Unnecessary comma in color() #266

Closed tabatkins closed 8 years ago

tabatkins commented 8 years ago

color()'s grammar is:

<ident> , <args> , <alpha>?, <fallback>?

The comma between the args and alpha is required, as we don't know how many args they are and both can be numbers. The comma between the fallback and the preceding stuff isn't strictly necessary, but it's pretty natural and matches up with other function's fallback arguments.

But the comma between the colorspace name and the arguments isn't necessary. There's no grammatical ambiguity possible, nor any strong tradition of comma usage in similar situations.

Can we remove it? Then we won't need any commas in the common case, like color(adobe-rgb 255 0 0).

svgeesus commented 8 years ago

As long as we don't allow the ident and the args to be freely intermixed, like color(127 foo 255 64). Because for named color profiles, the arglist includes identifiers not just numbers.

frivoal commented 8 years ago

Right, as long as the color profile name comes first, we're good. I'm in favor of Tab's proposed change (though I don't care strongly).

zcorpan commented 8 years ago

SGTM

grorg commented 8 years ago

I don't have a strong opinion, but I'd like to avoid a situation where we have this:

color(adobe-rgb 0.5 0.5 0.5, 0.5)

  1. That single comma looks weird, and people will have to remember to use it. Let's avoid a case where people have to think, even for a microsecond, whether they need to add a comma, and punish them if they get it wrong (e.g. gradients)
  2. As I commented on twitter, in the vast vast vast majority of these cases, the implementation and the author will know how many color parameters are necessary, and therefore where the alpha is.
  3. And in the very very very small set of cases where the implementation doesn't know how many color parameters are necessary, implementations will have to delay processing the parameters anyway. There is no point knowing the alpha before you know the color. And the page author isn't in much danger here - they were the ones who linked to the unusual profile.
  4. If the fallback is existing color syntax, then we don't need a separator. It starts with #, rgb or an ident.
tabatkins commented 8 years ago

Okay, you wanna be radical, let's be radical:

  1. Remove all commas from color().
  2. Drop that janky <alpha-value> nonsense, and just use <percentage> for the alpha. (Allowing multiple ways to write each arg is, I think, one of the mistakes of earlier color functions.)
  3. Add an srgb predefined colorspace.
  4. Let the colorspace be optional, and default to srgb.
  5. Also remove commas and restrict alpha to %s in all the new color functions: lab(), lch(), gray(), hwb().

We can then treat rgb()/rgba() as legacy syntaxes for specifying RGB colors (and hsl()/hsla() are already legacy, since hwb() is more intuitive for srgb stuff, and lch() is generally better overall).

(I'm not being facetious, I think this whole list is a great idea.)

frivoal commented 8 years ago

With the caveat expressed in https://github.com/w3c/csswg-drafts/issues/275#issuecomment-231311567 (if we make the color space optional, I'd prefer to keep the coma after it), I'm OK with Tab's proposal.

grorg commented 8 years ago

This is rad!

  1. đź‘Ť
  2. I don't like percentages. The rest of the industry uses 0-1 floats for all these values. I would like to just go with that. Think of the bytes we'd be saving! And the "5" keys!
  3. đź‘Ť
  4. đź‘Ť (which I guess is what I proposed in #275)
  5. See 2.
svgeesus commented 8 years ago
  1. meh. I think it is less readable if there is a long arglist. Consider color(foo, 0.2 0.3 0.4 0.5 0.6) is that a colorspace with five params, or one with four and an alpha? (note that this is a human readability argument, not a parsing difficulty argument)
  2. Agree with dinio, percentages here go against industry practice.
  3. yes
  4. yes
  5. see 2.
LeaVerou commented 8 years ago
  1. Given that color spaces can have any number of arguments, I think it's an important distinction for readability whether color(foo .1 .2 .3 .5) is a color with 3 params that is 50% opaque or a fully opaque color with 4 params.
  2. One of the tenets of good usability is the robustness principle: "be conservative in what you do and liberal in what you accept from others". Since there is no ambiguity, I think both percentages and floats should be allowed as alpha values. Especially since existing color functions accept floats, so not allowing them in color() is bound to create a ton of confusion.
  3. :+1:, in fact I believe this is one of the resolutions in the SF meeting.
  4. :+1:
  5. See 2.

I'm also not sure about dropping the comma between the color space and its params either. When things in CSS are space-separated, people expect to be able to use any order, unless there is an obvious ambiguity (e.g. all numbers). I think when there is a specified order, it's more clear to have a comma.

tabatkins commented 8 years ago

I... I don't understand what you people have against percentages. It's "industry practice" to use 0-1 floats because those languages (mostly C++) don't have any standard way to do typed-numbers and so just expose them as floats. CSS does have easy ways to do typed numbers, which are trivial to use and aid in readability.

Like, Lea says in her comment that color(foo .1 .2 .3 .5) is hard to read, because it's unclear whether the fourth number is a fourth arg to the colorspace or the alpha (I agree). But if you just write color(foo .1 .2 .3 50%), that problem completely disappears - it's now 100% obvious that the last one is an alpha, even at a casual glance. I argue that this is easier to read than color(foo .1 .2 .3, .5), and they're exactly the same number of characters, so the "save bytes" argument doesn't apply.

When things in CSS are space-separated, people expect to be able to use any order, unless there is an obvious ambiguity (e.g. all numbers). I think when there is a specified order, it's more clear to have a comma.

I don't think it's unusual to have "names" come before "arguments". Everything else about this function is precisely ordered already, and I somewhat agree with dino's argument that we shouldn't make it difficult to remember where the commas need to be.

bradkemper commented 8 years ago

One of the big criticisms of rgba() is that the 'a' part is in a different number format (0-1, vs. 0-255). If 'color()' with rgb and 'a' parts is similarly inconsistent (0%-100%, vs. 0-1), then it isn't an improvement on that front.

Commas are reasonable ways to separate groups of values in CSS. So are slashes, when commas won't do. I think having a distinctive separator on either side of the color components would be a good thing, and would improve readability. It would also mean we could allow the author to choose between 0-1 and 0%-100% for each of the r,g,b, and a components. That would be nice. Thus, the author could use all 0-1 for consistency with legacy alpha and between numbers, or use all percentages if they want.

svgeesus commented 8 years ago

I'm tempted to ask what "you other people" have against decimal number, but anyway. Percentages are fine where they are appropriate and natural. Percentages in rgb() were used purely because number without percent were already taken, and referred to a 0 to 255 scale, thus making the values of 0 and 1 ambiguous unless % was used as a flag.

svgeesus commented 8 years ago

Lea says in her comment that color(foo .1 .2 .3 .5) is hard to read, because it's unclear whether the fourth number is a fourth arg to the colorspace or the alpha (I agree). But if you just write color(foo .1 .2 .3 50%), that problem completely disappears - it's now 100% obvious

Right, until you write color(foo 10% .20% 30% 50%) and then it is hard to read again. A comma (or slash, as brad suggests) would help there.

atanassov commented 8 years ago

Based on the July 13, 2016 CSSWG conf call we have resolved to:

  1. All alpha for color functions can be <number> and <percentage>
  2. rgb() should be extended to allow an optional alpha. Likewise hsl() pending compat analysis on Tabatkins.

Remaining decision is about commas in color related functions with the following options:

  1. Require commas in all such functions
  2. commas are optional everywhere in color functions
  3. commas are optional in old funcs such as rbg() and drop the ones from new ones
  4. commas are required in old funcs such as rbg() and drop the ones from new ones
fantasai commented 8 years ago

There's also the remaining question, assuming we don't require commas in color(), of whether to use a slash as ChrisL suggests.

fantasai commented 8 years ago

I'm currently leaning towards no commas in color(), since the syntax allows keywords, has optional arguments, etc. which are more CSSy than traditional functions. But I also lean towards requiring the slash for readability, since as ChrisL points out, it's hard to visually parse a color consisting of all percentages. And imho percentages are the most natural way to represent a range between 0 and 1, so they should be comfortable to use for all of the color coordinates.

[While we're at this, btw, we should also review all our other functions for consistency. The comma-less syntax we're discussing here is consistent with the attr() function in V&U, but Grid's repeat(), for example, probably doesn't need its comma either.]

bradkemper commented 8 years ago

I was thinking of something along these lines, to avoid multiple nesting of color() functions in the fallback:

[<color-channel-args>[/<alpha>||<ident>]?]# [, <non-color()-fallback>]?

bradkemper commented 8 years ago

Or variations on this theme:

[[<ident>/]? <color-channel-args>[/<alpha>]?]# [, <non-color()-fallback>]#

Thus, commas only used for fallback list. By the way, the spec doesn't seem to say when fallback is used, and I wasn't in that conversation of the f2f. Is it when a color profile can't be used because it doesn't fit within the gamut of the device?

tabatkins commented 8 years ago

it's hard to visually parse a color consisting of all percentages.

Nothing but the alpha can be a percentage. The rest are numbers.

And imho percentages are the most natural way to represent a range between 0 and 1, so they should be comfortable to use for all of the color coordinates.

The color coordinates aren't necessarily 0-1. That's common, but there's nothing requiring that. They're not appropriate to be percentages.

I was thinking of something along these lines,

There's no need for any of those slashes; they serve no purpose for disambiguation. And there's no need for a comma separating the fallback from the rest, for the same reason. color() is also allowed in fallback, intentionally - you can fallback to a different colorspace.

Is it when a color profile can't be used because it doesn't fit within the gamut of the device?

No, it's for when a color profile is invalid or unknown. Color profiles already define how to scale themselves down to fit smaller gamuts.

Crissov commented 8 years ago

Y’know, for subjective arguments like “easier to read” there are actually rather objective empirical tests possible. Standardizing bodies don’t have a great history of conducting user studies, though: it’s usually the best (or loudest) articulated argument in a committee of experts that decides such issues. (That’s not saying that corpus analyzes of existing content didn’t help to back one’s argument already.)

jensimmons commented 8 years ago

I just posted this as a question on Twitter. The overwhelming consensus is that people want commas. https://twitter.com/jensimmons/status/753664695563943936

"I'd be ok with optional commas, but mandatory absence of commas seems like it would constantly trip me up." — Zing Web Creak

"I l like commas, it's consistent with other functions" — Ire Aderinokun

"It's one thing to accept a version without commas, but commas are standard, so shouldn't cause an error." — Estelle Weyl

"I prefer commas, both for consistency and because they’re an extra visual cue that can help avoid errors." — Eric Meyer

[Replying to Eric Meyer] "Agreed. There’s less ambiguity there." — Aaron Gustafson

[Replying in the same thread] "With you all: commas - if it looks like bunch of parameters being passed, separate them. Also, lessens the cognitive load when everything has the same convention [rgb(), translate3d(), etc]" — Chris Casino

[And Eric again, replying] "Yep. I already struggle with the syntax of circle() and ellipse(), which don’t allow commas. " — Eric Meyer

One person did say: "no commas, just like { padding: 1em 0 2em; }" — Chris Johnson but he was the only one who thought no commas is more consistent with the rest of CSS. Most others argued that having commas is more consistent with the rest of CSS.

You can find more replies by clicking the above link. Many were simple "yuk" statements, in response to the code snippet I included that showed no commas.

plinss commented 8 years ago

I don't think the way that your informal twitter survey was phrased is accurately portraying the choice we're trying to make. You listed old functions with commas and a new function without, where the predominant proposals on the table are to have optional commas in the old functions and possibly optional commas in the new function. There's also no comparison to other css functions that already don't allow commas between certain arguments. I suspect this is biasing the results.

Maybe try again with something like: repeat(2, [a] 1fr [b]); circle(at 50% 50%); rgba(255, 255, 255, 0.6); hsl(240, 100%, 50%); newcolorfunction(.1, .2, .3, 50%);

vs:

repeat(2, [a] 1fr [b]); circle(at 50% 50%); rgba(255 255 255 0.6); hsl(240 100% 50%); newcolorfunction(.1 .2 .3 50%);

bradkemper commented 8 years ago

There's no need for any of those slashes; they serve no purpose for disambiguation. And there's no need for a comma separating the fallback from the rest, for the same reason. color() is also allowed in fallback, intentionally - you can fallback to a different colorspace.

Having multiple nestlings of color() in order to get multiple fallbacks is exactly what I don't like about that. It's ugly and unreadable. It would be better, and more consistent with other CSS, if commas were used for separating the multiple fallbacks WITHOUT having to repeat the color( on one side and ) on the other. And once you do that, then comma is no longer available as a visual separator of the subgoubings. Thus, a slash instead.

bradkemper commented 8 years ago

Imagine if for font-family fallbacks, we had to write something like this:

font-family: font-family(helvetica font-family(arial font-family(sanserif)));

That's basically in line with what you want for color: color() fallbacks.

bradkemper commented 8 years ago

I think this is more readable:

color: color(bradsMonitorRGB/20 30 40, AdobeRGB/21 29 41, AdobeRGB1998/21 32 40, sRGB/22 32 45)

(Numbers are just made up for this example)

LeaVerou commented 8 years ago

@bradkemper If there are commas, what's the point of the slash?

bradkemper commented 8 years ago

@leaverou what I proposed is that the commas are separating compound values in a list of fallbacks (color() values, not complete functional notations of color()). So if they are used for that, then they shouldn't be used within each compound value in that list. That would be confusing, and harder to read. So a slash would be a separator of the sub-value groups, to group and separate the color channels from the alpha and/or profile identifier.

Maybe a separator would not be necessary for the profile identifier, but it would help distinguish the alpha from the other component values (without making the alpha seem like a separate item in the list of fallbacks).

Once you do have a slash, you can put alpha and profile identifier on one side of the slash, and color channels on the other.

bradkemper commented 8 years ago

Note how with image-set(), for example, the commas are just for separating items in a list, where each item is itself a compound value. The list of color stops inside linear-gradient() is similar. If an additional separator was needed within each item, I wouldn't expect it to be another comma.

LeaVerou commented 8 years ago

That sounds like yet another reason to separate alpha with a comma. There is no reason to keep re-specifying the same alpha value for each fallback.

bradkemper commented 8 years ago

Oh, I see. Just have it there once? I hadn't considered that. It'd be a little odd, having it on one end or the other of a list of fallbacks, but but I guess that'd work. I'm used to seeing it right after the color channels (in rgba(), etc.), so it seems more natural to me to travel with each set of color channels. Hmm.

bradkemper commented 8 years ago

I sometimes do this sort of thing, for abs pos stuff like menus or tool tips::

background: rgba(255,255,255,0.95)

That gives me a white background with just enough translucency to tell that there is stuff behind it, without interfering too much with the readability of the text in front of it.

Might I not want to set a lower alpha opacity to the color for a color space that tended to blow out the lighter colors, so that I could still see some lighter gray patterns through it? If so, then I'd want the alpha associated with the color space.

fantasai commented 8 years ago

So far my favorite proposal here is color( [ <colorspace>? <number>+ [ / <alpha> ]? ]# ). I think Brad's suggestion of using commas for the fallbacks--and therefore not internally to a color--makes sense. I agree that nesting color functions is awkward. There's no reason not to just use commas here and indicate rgb or hsl with those keywords--it's easier to type, works better when chained, and is consistent across all arguments: color(adobeRGB 0.2 0.6. 0.4 / 100%, rgb 0.2 0.67 0.5 / 100%)

Since we'd also want to allow keywords and #rgb notation, the full proposal then would be

color(  [ <colorspace>? <number>+ [ / <alpha> ]? ]# [ , <color> ]? )

I could go either way on the slash if indeed only <alpha> can take a percentage, but I'm not convinced that's a good restriction to introduce for all time.

frivoal commented 8 years ago

This proposal makes sense to me, but since we also want to support named colors from an icc profile, it should probably be amended to this:

color( [ <colorspace>? [ <number>+ | <string> ] [ / <alpha> ]? ]# )

or

color(  [ <colorspace>? [ <number>+ | <string> ] [ / <alpha> ]? ]# [ , <color> ]? )
LeaVerou commented 8 years ago

I'm very skeptical about the slash. It's completely different than what any other color function is doing. Also we've seen in other microsyntaxes that if something is separated by a slash, authors don't use it very much (e.g. border-radius, background shorthand). The only exception is the font shorthand, because there it was common practice before CSS to refer to [font-size]/[line-height] so it was externally consistent.

bradkemper commented 8 years ago

@fantasai @frivoal I'd support that variation.

@leaverou I don't think having a slash would equate to authors not wanting to use it. With border-radius, it just isn't needed that much because the vast majority of border radii are symmetrical with regard to vertical and horizontal. Also, it is a little confusing and harder to remember that when specifying the two axes on a single corner (with border-top-left-radius for instance), no slash is required between the two values, but when using the border-radius shorthand, you do need a slash between the two axis values.

With the background shorthand, there was a long time when background shorthands didn't support inclusion of background-size, and authors got used to writing it separately. Even today, it is often a separate thought to change the default size of the background size (at least for me, when I use it). It is also often useful to be written in a separate, more general rule when the value is 'contain', for instance, while more specific rules change the image source. There may also be some authors who can't remember where to put the slash and background-size value in the shorthand, because it is such a long, complex syntax.

I don't think any of these issues would apply to color(). Font shorthand shows that authors are not adverse to using a slash when it makes sense and is easy to do so.

bradkemper commented 8 years ago

@fantasai I really like your idea of using e.g. hsl (and presumably hwb, lab, lch, gray, etc.) as just another colorspace keyword in the color() value. It makes me wonder if we really need separate functions for hwb, lab, lch, gray, etc.

In other words, why have lab(50,80,160) if you already have color(lab 50,80,160)? It would be one color function to rule them all! That would make the syntax very simple to remember, too, because it would be such a consistent way to write colors (even though it is a few extra characters).

bradkemper commented 8 years ago

Oops. I meant to say:

Why have lab(50,80,160) (or lab(50 80 160)) if you already have color(lab 50 80 160)?

FremyCompany commented 8 years ago

Ok, we seem to be in full bikeshed mode already, so did we consider

color(hsl 50% 50% 50% a 50%)

?

That seems more clear than a slash or comma that seems to be lost there in the middle of the function (at least that's the impression it gives me when I see it). It would also be a nice notation for other things like

color(rgb $r $g $b h 180deg)

to invert the hue of a color itself defined in rgb for instance.

bradkemper commented 8 years ago

I'm not too keen on one-letter abbreviations. Would 's' stand for 'saturation' or 'shade'? I find the whole color-mod section of the spec to be confusingly written, and I don't like the OLAs there either. But I would be good with having something like color(rgb 255 0 0 saturation -50%). And if we had that, then I guess color(rgb 255 0 0 alpha 50%) makes sense. Though it is then a lot longer than rgb(255 0 0 / 50%). But, it's pretty darn clear.

FremyCompany commented 8 years ago

I wouldn't mind alpha written out entirely, that gives a lot of clarity, but at the same times I think everyone knows that colors have an addition alpha parameter, and a could be clear enough.

You're right for saturation/shade/etc though, I didn't think this through but yes full identifiers make a lot of sense there.

Crissov commented 8 years ago

Note, though, that alpha is a completely arbitrary designation and thus opaque to newcomers. Pun intended.

bradkemper commented 8 years ago

@crissov good point.

color(rgb 255 0 0 opacity 50%)

On the other hand, opacity/alpha in color is so common, I'd rather use a slash (or even a space, if I can't have that) than to require authors to type out a spelled out word for that. I can't imagine that much added typing would make it popular.

bradkemper commented 8 years ago

Maybe we should go ahead and have colons in the functional syntax, after the color-mod name. Something like this:

color( [ <colorspace>? [ <number>+ | <string> ] [ / <alpha> ]? [<color-mod-name>: <color-mod-value>]* ]# )

fantasai commented 8 years ago

We don't really put colons inside a CSS value syntax, and I think the leading keyword is sufficiently clear. I agree with Brad that using a slash for alpha makes more sense given how common it is.

Wrt replacing all the color functions with color().... that's a lot of unnecessary typing. :) So long as a color doesn't need fallbacks, using its own function is more ergonomic and should therefore be allowed.

tabatkins commented 8 years ago

Looks like it was decided in the telcon today to accept fantasai's suggested syntax:

color(  [ <colorspace>? [ <number>+ | <string> ] [ / <alpha> ]? ]# , <color>? )

(I'm happy with this; the comma usage is to separate fallbacks, which corresponds to the "separating repetitions" usage that CSS normally uses commas for. I still don't think the slash is necessary, but I'm fine with having it, and doing so means it's clear where the alpha is even if you use a <number> for alpha, or if we in the future allow the colorspace arguments to be <percentage>s.)

svgeesus commented 8 years ago

Yes, that is what was decided today and it enables multiple fallbacks without having to use multiple nested functions. The slash is not necessary for a parser and is very helpful for human readers, especially once we get colorspaces with many parameters (such as multi-ink printers).

tabatkins commented 8 years ago

So this suggests that the comma-less rgb()/etc syntax we agreed to add should also use the / to separate the alpha?

fantasai commented 8 years ago

Yes, exactly. See the minutes.

tabatkins commented 8 years ago

Thanks, I hadn't read them completely thru yet. Editting now.

bradkemper commented 8 years ago

We didn't actually resolve on the parentheses-less color-mod part, but I didn't hear anything against it when I mentioned it. I hope that can go into the next editors draft too.

Also no clear resolution on if we want e.g. rgba(<r>, <g>, <b>) and rgb(<r>, <g>, <b>, <a>) (probably not?), but we do want rgb(<r> <g> <b> / <a>).

I don't care about rgba(<r> <g> <b> / <a>), if that's included in the resolution or not. I'd prefer to only back-port to the non-a functions, since the /<a> would be available in rgb().