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-color] Unnecessary comma in color() #266

Closed
tabatkins opened this Issue Jul 4, 2016 · 61 comments

Comments

Projects
None yet
@tabatkins
Copy link
Member

tabatkins commented Jul 4, 2016

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

This comment has been minimized.

Copy link
Contributor

svgeesus commented Jul 5, 2016

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

This comment has been minimized.

Copy link
Collaborator

frivoal commented Jul 6, 2016

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

This comment has been minimized.

Copy link
Member

zcorpan commented Jul 6, 2016

SGTM

@grorg

This comment has been minimized.

Copy link
Contributor

grorg commented Jul 6, 2016

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

This comment has been minimized.

Copy link
Member

tabatkins commented Jul 6, 2016

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

This comment has been minimized.

Copy link
Collaborator

frivoal commented Jul 8, 2016

With the caveat expressed in #275 (comment) (if we make the color space optional, I'd prefer to keep the coma after it), I'm OK with Tab's proposal.

@grorg

This comment has been minimized.

Copy link
Contributor

grorg commented Jul 10, 2016

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

This comment has been minimized.

Copy link
Contributor

svgeesus commented Jul 10, 2016

  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

This comment has been minimized.

Copy link
Contributor

LeaVerou commented Jul 10, 2016

  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. 👍, in fact I believe this is one of the resolutions in the SF meeting.
  4. 👍
  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

This comment has been minimized.

Copy link
Member

tabatkins commented Jul 11, 2016

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

This comment has been minimized.

Copy link

bradkemper commented Jul 12, 2016

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

This comment has been minimized.

Copy link
Contributor

svgeesus commented Jul 13, 2016

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

This comment has been minimized.

Copy link
Contributor

svgeesus commented Jul 13, 2016

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

This comment has been minimized.

Copy link
Collaborator

atanassov commented Jul 13, 2016

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

This comment has been minimized.

Copy link
Contributor

fantasai commented Jul 13, 2016

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

@fantasai

This comment has been minimized.

Copy link
Contributor

fantasai commented Jul 13, 2016

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

This comment has been minimized.

Copy link

bradkemper commented Jul 14, 2016

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

This comment has been minimized.

Copy link

bradkemper commented Jul 14, 2016

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

This comment has been minimized.

Copy link
Member

tabatkins commented Jul 14, 2016

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

This comment has been minimized.

Copy link
Contributor

Crissov commented Jul 14, 2016

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

This comment has been minimized.

Copy link

jensimmons commented Jul 14, 2016

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

This comment has been minimized.

Copy link
Member

plinss commented Jul 14, 2016

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

This comment has been minimized.

Copy link

bradkemper commented Jul 14, 2016

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

This comment has been minimized.

Copy link

bradkemper commented Jul 14, 2016

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.

@FremyCompany

This comment has been minimized.

Copy link
Contributor

FremyCompany commented Jul 21, 2016

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

This comment has been minimized.

Copy link
Contributor

Crissov commented Jul 25, 2016

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

@bradkemper

This comment has been minimized.

Copy link

bradkemper commented Jul 26, 2016

@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

This comment has been minimized.

Copy link

bradkemper commented Jul 26, 2016

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

This comment has been minimized.

Copy link
Contributor

fantasai commented Jul 27, 2016

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

This comment has been minimized.

Copy link
Member

tabatkins commented Jul 27, 2016

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.)

@tabatkins tabatkins added ready and removed Agenda+ labels Jul 27, 2016

@svgeesus

This comment has been minimized.

Copy link
Contributor

svgeesus commented Jul 27, 2016

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

This comment has been minimized.

Copy link
Member

tabatkins commented Jul 27, 2016

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

@fantasai

This comment has been minimized.

Copy link
Contributor

fantasai commented Jul 27, 2016

Yes, exactly. See the minutes.

@tabatkins

This comment has been minimized.

Copy link
Member

tabatkins commented Jul 27, 2016

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

@tabatkins tabatkins closed this Jul 27, 2016

@tabatkins tabatkins removed the ready label Jul 27, 2016

@bradkemper

This comment has been minimized.

Copy link

bradkemper commented Jul 28, 2016

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().

@tabatkins

This comment has been minimized.

Copy link
Member

tabatkins commented Jul 28, 2016

Yeah, I haven't touched color-mod() yet.

We previously agreed to make rgb() and rgba() both 3-arg-with-optional-alpha, so I kept that in. If we decide we don't want that change to the old syntax, we can rip it out later.

I do care about the simplicity of just making rgba() an alias for rgb(). Remember tha any differences mean implementation effort has to be expended to parse them differently; I don't think there's a strong enough principled reason to justify a difference.

I might rearrange things to only put the "rgb(r g b / a)" syntax up front, and move all the others (rgb(r, b, g), rgba() in general) to the bottom of the section and describe them as legacy compat.

@valtlai

This comment has been minimized.

Copy link

valtlai commented Jul 28, 2016

Could the slash be optional? I think rgb(r g b a) is clear enough.

Edit: Is this only for W3C members and invited experts, or can anyone participate?

@tabatkins

This comment has been minimized.

Copy link
Member

tabatkins commented Jul 28, 2016

No, we need the slash in color() (because it takes a variable number of colorspace parameters, making it difficult to tell whether there's an alpha or not), and it's worthwhile to make everything use the same syntax to aid in learning. (It's very annoying and error-prone, even for experienced people, if very similar constructs use subtly different syntaxes.) All the 3/4-argument functions don't really need the slash, I agree, but it doesn't hurt either; it just make it extra-obvious that there's an alpha or not.

@svgeesus

This comment has been minimized.

Copy link
Contributor

svgeesus commented Jul 28, 2016

I might rearrange things to only put the "rgb(r g b / a)" syntax up front, and move all the others (rgb(r, b, g), rgba() in general) to the bottom of the section and describe them as legacy compat.

I like that idea

@frivoal

This comment has been minimized.

Copy link
Collaborator

frivoal commented Jul 29, 2016

Is this only for W3C members and invited experts, or can anyone participate?

Anyone can participate, and feedback is very welcome.

@LeaVerou

This comment has been minimized.

Copy link
Contributor

LeaVerou commented Dec 8, 2016

I know we've resolved on this now, and I hate to reopen this can of worms, but after seeing a few code examples with the new syntax, I'm having serious doubts that this was a good decision. It's fairly hard to tell where the parameter boundaries are, especially with certain fonts and characters, and especially when commas and decimal points are also present. E.g. look at these examples:

color: lch(55.3 84.5 10.25, #F06);
fill: lch(85.5 43.1 79.1);
color: lab(55.3 83.1 15.0 / 75%, #F06);

compare with:

color: lch(55.3, 84.5, 10.25, #F06);
fill: lch(85.5, 43.1, 79.1);
color: lab(55.3, 83.1, 15.0, 75%, #F06);

(using the variable width font on purpose)

Is it just me, or do your eyes struggle less to separate the parameters in the second example?

@frivoal

This comment has been minimized.

Copy link
Collaborator

frivoal commented Dec 8, 2016

Comma have (are?) a descender, while the rest of the characters used don't (unless you use old style figures). So yes, they absolutely do stick out more and mark a stronger visual separation. However, to me the question is not that, it is whether there is sufficient separation when the comas are removed, and I think that this is the case.

As far as I am concerned, even with the commas, a string of numbers like this is something that:

  • as a whole, stands out visually, and is easy to identify in the middle of a longer piece of text/code
  • in detail, is something I'll have to parse rather than read, and space separation seems sufficient to me to identify the components with no more effort than it takes to parse them anyway.

Tongue in cheek: if we want to go for clear visual separation, I suggest this:

color: lch(55.3 🍡 84.5 🍡 10.25, #F06);
fill: lch(85.5 🍡 43.1 🍡 79.1);
color: lab(55.3 🍡 83.1 🍡 15.0 / 75%, #F06);

@Crissov

This comment has been minimized.

Copy link
Contributor

Crissov commented Dec 8, 2016

I may be repeating myself here, but unitless numbers are evil.

PS: #278

color: lch(55.3% 84.5 10.25deg, #F06);
fill: lch(85.5% 43.1 79.1deg);
color: lab(55.3% 83.1 15.0 / 75%, #F06);

The current level-4 draft doesn’t support a comma-separated <color> fallback in lch() and lab(), but only in color(), by the way.

@LeaVerou

This comment has been minimized.

Copy link
Contributor

LeaVerou commented Dec 8, 2016

@frivoal I know that if I look at them carefully I can tell where the boundaries are. But code is skimmed A LOT, and even a second of delay in that can be very frustrating, since there is a lot to skim.

As an experiment, try to change your editor color scheme, or font, or even font size, and try to code. Sure, you can still read the code, but the slight delay this change has introduced is very frustrating until you get used to it, isn't it? It's similar with the lack of commas, except the delay introduced won't be completely eliminated by getting used to it. You can still separate the parameters, but it takes more time and cognitive load, which for very frequent tasks becomes frustrating.

@svgeesus

This comment has been minimized.

Copy link
Contributor

svgeesus commented Dec 15, 2016

The current level-4 draft doesn’t support a comma-separated fallback in lch() and lab(), but only in color(), by the way.

Good point, those should support a fallback too.

@bradkemper

This comment has been minimized.

Copy link

bradkemper commented Dec 18, 2016

@frivoal I know that if I look at them carefully I can tell where the boundaries are. But code is skimmed A LOT, and even a second of delay in that can be very frustrating, since there is a lot to skim.

When I'm skimming, I'd prefer the separation between the fallbacks to be what jumps out at me first, and the alphas to be more easily distinguished than the color channels. If I really want to hand edit the color channels, then I can, but I'm more likely going to want to hand-tweak the alpha, or change something in the fallback list. So I like it with the separators we resolved on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment