Skip to content
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-4] L as <percentage> in lab() and oklab() #6761

Closed
danburzo opened this issue Oct 25, 2021 · 12 comments
Closed

[css-color-4] L as <percentage> in lab() and oklab() #6761

danburzo opened this issue Oct 25, 2021 · 12 comments

Comments

@danburzo
Copy link

danburzo commented Oct 25, 2021

In the context of adding oklab() and oklch() syntaxes, which use <percentage> for the L channel that maps to its canonical [0, 1] range, the decision to use <percentage> for the L in lab() and lch() (previously discussed) to map to its canonical [0, 100] range feels inconsistent.

Since the spec has recently included a breaking change in the definition of color(xyz), now might be a good opportunity to revisit the decision to use <percentage> in lab() and lch()? (Safari 15 being the only major browser to have shipped all these things.)

The decision also impacts the usage of color() if these color spaces will be allowed as identifiers, since you're allowed to use either <number> or <percentage> for the Lightness channel.

I think the simplest way forward is to let l, a, and b in lab() and oklab() all be <number>s on their own scale. Authors already have to consult reference materials for the useful range of a/b/chroma for each theoretically-unbounded color space, so the L as percentage abstraction doesn't do much to ease that, while making interop with non-web applications (if not the Color API) less straightforward.

I don't think consistency with hsl() in regards to lightness being a percentage is worth breaking the <number> = <percentage> / 100 convention present throughout the other color syntaxes.

@tabatkins
Copy link
Member

I'm not sure I understand what you're finding confusing/inconsistent. If anything, having L use percentages to refer to the range of the component in both cases seems vastly more consistent than forcing authors to remember that L in one case is 0-1 and in another, seemingly identical, case is 0-100 (and in a third case is a 0%-100% range)

Note that %s referring to a 0-1 range is not consistent thruout the color syntaxes. In rgb(), for example, it covers a 0-255 range. %s are just doing what they're supposed to do - express fractions of a contextually-relevant maximum value (or suitable "unit" value) - same as everywhere else in CSS (and outside of CSS) that uses %s.

I'd be strongly against losing the ability to use %s for such an obvious "0 to max" range case. I wish we could use them in the a/b/chroma channels too!

@danburzo
Copy link
Author

danburzo commented Oct 26, 2021

Thanks for this perspective. I guess(?) I was fixated on percentages resolve to 0.0 .. 1.0 except that for lab the first percentage resolves to 0.0 to 100.0 and overestimated the impact of this "lab exception", whereas in color() percentages, like everywhere else, (can, and do) simply refer to the range of components, whatever those might be.

With this interpretation of <number-percentage> in color(), is there still an impediment in allowing <number-percentage> in the L component of CIE/OKLab & LCh? It would mean that the percent-to-number mapping be made explicit for each color space, the same as if they made it in the color() syntax (perhaps the change is only warranted if the latter happens?). A nice-to-have for better interop, not necessarily needed if it's accessible via the color() syntax.

@svgeesus
Copy link
Contributor

svgeesus commented Oct 26, 2021

I'm not sure I understand what you're finding confusing/inconsistent. If anything, having L use percentages to refer to the range of the component in both cases seems vastly more consistent than forcing authors to remember that L in one case is 0-1 and in another, seemingly identical, case is 0-100 (and in a third case is a 0%-100% range)

Here, let me help.

A color has the following color coordinates (say I found these in some other program and want to use them in CSS)

CIE Lab87.82-79.380.99
CIE LCH87.82113.3134.4
OK Lab0.866-0.2300.179
OK LCH0.8660.291142.1

So I have to remember that CSS treats CIE Lightness as a percentage, and that percentages in CIE Lightness map to 0-100 in CSS so okay I slap on a percent sign; but I just use the numbers as-is

lab(87.82% -79.3 80.99)
lch(87.82% 113.3 134.4)

Now I want to use the OK values so I remember that in CSS (as currently specified) I have to slap on a percent and also multiply by 100

oklab(0.866 -0.23 0.179)

Edit: I actually forgot to do this when typing the example

oklab(86.6% -0.23 0.179)

and then I do the OKLCH an, um, does Chroma get multiplied by 100 too? Does it get a percent? If so, that is a percent of what? Wait should I be scaling a and b by 100 too? If so should Chroma maybe be scaled by 141.42 (square root of 100^2 + 100^2)?

oklch(86.6% 29.1 142.1)
oklch(86.6% 29.1% 142.1)
oklch(86.6% 41.15 142.1)
oklch(86.6% 0.291 142.1)

Currently, for CSS Color 4 the last of those is correct. The chance that people mess this up in one way or another and end up with an off-by-100 error when simply copying colors into CSS is large.

@Crissov
Copy link
Contributor

Crissov commented Oct 26, 2021

The way to fix this on the CSS side is (and always had been) to require units for all components in all predefined color notations (e.g. lch()). Yes, this includes deg (or another angular unit) for hue and probably % for chroma and, of course, % for alpha/opacity.

For the lower-level color() notation, on the other hand, CSS could accept whatever is conventional outside CSS.

@tabatkins
Copy link
Member

So I have to remember that CSS treats CIE Lightness as a percentage, and that percentages in CIE Lightness map to 0-100 in CSS so okay I slap on a percent sign; but I just use the numbers as-is
[...]
Now I want to use the OK values so I remember that in CSS (as currently specified) I have to slap on a percent and also multiply by 100

I think you're vastly overcomplicating the mental model people will be using here! I suspect the overwhelming majority of authors will just go "0% lightness is black, 100% lightness is white, values between are colors of varying lightness levels", because that's how lightness already works in HSL, which is the color function closest to Lab/etc that's been in use for decades (and it accords with how percentages work everywhere else in CSS and generally in non-CSS contexts too).

You appear to be asserting that it's simpler for authors to remember "in HSL lightness is 0%-100%, in Lab/LCH lightness is 0-100, in oklab/oklch lightness is 0-1", despite all three assigning identical meanings to the endpoints? Like, I get it that if you're copying colors straight from some other application it would be nice to just be able to paste them into the parentheses unchanged, but to assert that this is a simpler mental model seems frankly untenable to me.

(I think it's weird that Lab used a rough 0-100 range for the past 50 years while OKLab, intended as a successor which is more or less indistinguishable in most cases, chose to instead scale its numbers to a rough 0-1 range, but hey, I didn't design it. I guess it's just an annoying quirk authors will have to learn.)

and then I do the OKLCH an, um, does Chroma get multiplied by 100 too? Does it get a percent? If so, that is a percent of what?

I mean, it's not unreasonable for people to assume that chroma, like HSL's saturation, is written as a percentage, but in practice it doesn't have a useful max value as a range. Unfortunate, but that's how it is. It's just an oddity of colors that authors will have to learn; it's a syntax error if they screw it up so at least it's immediately obvious a mistake has been made, and it'll show up as an error in devtools.

But yeah, it's already gonna be confusing that lch()'s chroma maxes out in the vicinity of 100 while oklch()'s chroma maxes out in the vicinity of 1.

I don't see how the rest of your questions are even remotely believable as an author's thought pattern, so I won't address them.

@weinig
Copy link
Contributor

weinig commented Nov 27, 2021

As a point of experience, though no real thought out opinion, when implementing support for OKLab in WebKit this week, this bit me briefly (all the resulting colors were very extremely white) and needed to add a divide by 100.

The only real downside of using a percentage here in my mind is if it trips users up when pasting in component values from another tool (say, something like https://colorjs.io/apps/convert/?color=lime&precision=4 which uses the unit notation).

One concrete thing you might want to consider is updating the sample code to call out this difference.

@svgeesus
Copy link
Contributor

oklch(86.6% 29.1 142.1)
oklch(86.6% 29.1% 142.1)
oklch(86.6% 41.15 142.1)
oklch(86.6% 0.291 142.1)

Currently, for CSS Color 4 the last of those is correct. The chance that people mess this up in one way or another and end up with an off-by-100 error when simply copying colors into CSS is large.

I literally just made that mistake (again) when adding the section on resolving OKLab and OKLCH values.

The only real downside of using a percentage here in my mind is if it trips users up when pasting in component values from another tool

Yes, this is exactly my concern

(say, something like https://colorjs.io/apps/convert/?color=lime&precision=4 which uses the unit notation).

That tool can be and should be updated, because it primarily tracks CSS and other Web Platform rather than color in general. But other tools will present OKLab values as defined in the OKLab specification which frankly should not surprise anyone.

One concrete thing you might want to consider is updating the sample code to call out this difference.

Good idea.

@svgeesus
Copy link
Contributor

We do already have

Like CIE Lab, there is a central lightness axis which is usually written as a unitless number in the range [0,1]; for compatibility with the rest of CSS, it is written as a percentage. 100% means an L value of 1.0. L=0% is deep black (no light at all) while L=100% is a diffuse white.

@svgeesus
Copy link
Contributor

svgeesus commented Feb 2, 2022

I expect to further clarify this aspect once #6741 is resolved (assiming we resolve to add the reference percentage ranges)

@svgeesus
Copy link
Contributor

Noting that i Just fixed Minor typo in css-color-4 example 6 which was caused by a percentage on Chroma ( added here) although in that example it was 0 so the scaling was unclear.

@svgeesus
Copy link
Contributor

Spec is now updated: Lab and OKLab

@svgeesus
Copy link
Contributor

One concrete thing you might want to consider is updating the sample code to call out this difference.

In addition to the clarified spec prose, the sample code now has, for OKLab:

return multiplyMatrices(LMStoOKLab, LMS.map(c => Math.cbrt(c)));
	// L in range [0,1]. For use in CSS, multiply by 100 and add a percent

and for CIE Lab:

return [
		(116 * f[1]) - 16, 	 // L
		500 * (f[0] - f[1]), // a
		200 * (f[1] - f[2])  // b
	];
// L in range [0,100]. For use in CSS, add a percent

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

No branches or pull requests

5 participants