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

Carrying forward analogous components in color interpolation #473

Open
svgeesus opened this issue Mar 6, 2024 · 6 comments
Open

Carrying forward analogous components in color interpolation #473

svgeesus opened this issue Mar 6, 2024 · 6 comments
Assignees
Labels
spec-parity Parity with the CSS Color specs

Comments

@svgeesus
Copy link
Member

svgeesus commented Mar 6, 2024

@svgeesus So this seems like a surprising revelation. In the past, the resolution of undefined values seemed to be handled during the actual interpolation, not before. Currently, this is exactly how Color.js handles things. Below, we do not get 270deg, but carryforward is also not implemented.

> new Color("hsl(90deg 50% 50%)").mix('hsl(none 50% 50%)', {space: 'hsl', hue: 'longer'});
Color {
  space: <ref *1> ColorSpace {
    id: 'hsl',
    name: 'HSL',
    base: RGBColorSpace {
      id: 'srgb',
      name: 'sRGB',
      base: [RGBColorSpace],
      aliases: undefined,
      fromBase: [Function: fromBase],
      toBase: [Function: toBase],
      coords: [Object],
      white: [Array],
      formats: [Object],
      referred: 'display',
      path: [Array]
    },
    aliases: undefined,
    fromBase: [Function: fromBase],
    toBase: [Function: toBase],
    coords: { h: [Object], s: [Object], l: [Object] },
    white: [ 0.9504559270516716, 1, 1.0890577507598784 ],
    formats: { hsl: [Object], hsla: [Object] },
    referred: undefined,
    path: [ [ColorSpace], [RGBColorSpace], [RGBColorSpace], [Circular *1] ]
  },
  coords: [ 90, 50, 50 ],
  alpha: 1
}

But now, the act of carrying forward, which I was under the impression carried forward undefined values only, is also now resolving undefined values with the context of colors before and after it and no longer waiting for interpolation which seemed to be what was implied here?

If a color with a carried forward missing component is interpolated with another color which is not missing that component, the missing component is treated as having the other color’s component value.

Has Color.js always been wrong, or does carryforward fundamentally change this? I have to admit, I can see why browsers may be doing this differently than others. I find it unintuitive that carryforward would resolve the undefined values in this manner.

Originally posted by @facelessuser in w3c/csswg-drafts#9224 (comment)

@svgeesus svgeesus added the spec-parity Parity with the CSS Color specs label Mar 6, 2024
@facelessuser
Copy link
Collaborator

I'll have a fix-up later today. I had to make changes my lib conform when explicitly requested to do so. I have it working in Color.js locally:

> new Color("hsl(90deg 50% 50%)").mix('hsl(none 50% 50%)', {space: 'hsl', hue: 'longer'}).coords
[ 270, 50, 50 ]

@facelessuser facelessuser self-assigned this Mar 7, 2024
@facelessuser
Copy link
Collaborator

I guess I should clarify the fix will contain the longer fix, not implement carry forward. Carry forward will require some thought on if/how Color.js wants to abstract it generally.

@facelessuser
Copy link
Collaborator

Hue handling now matches CSS per #474 which is now on main.

Carry forward has not been implemented. This will need consideration of how to implement it within this library and to make it generic enough for all the spaces currently supported. I will leave this issue open for that purpose.

@svgeesus
Copy link
Member Author

in 12.2. Interpolating with Missing Components

The analogous components are as follows:

Category Components
Reds r,x
Greens g,y
Blues b,z
Lightness L
Colorfulness C, S
Hue H
Opponent a a
Opponent b b

So these are already fairly broad categories (eg XYZ is treated as an RGB space with super-saturated, unreal primaries which is correct but not immediately obvious) so it seems the questions are:

  1. Are there any color spaces in Color.js which are not covered by one of these categories? If so should we add to these categories or say that they are not analogous?
  2. Should carry-forward be default but opt-out, non-default and opt-in, or just enabled regardless? I'm slightly tending to default but opt-out.

@facelessuser
Copy link
Collaborator

Currently, the only questionable one I can think of is HPLuv. P is a saturation like coordinate, but not really the same thing. I can't recall if I tested it special though, I may have just declared it an HSL type in my library.

I don't know that all spaces are universally named though in color.js. I think there needs to be an interface that allows a color to be identified generally as LCh like, Lab like, etc. And then a way to grab the coordinates in a common way. At least that is how I approached it in my library. Oh HSV could share V with other HSV, but doesn't have to.

One issue, JzCzhz is accessed as jz, if I recall correctly. It may be nice if it could be accessed as c as well. Oh, and CAM16 currently uses m.

Another approach, and one that I use, is all LCh like spaces could have a way to pull the coordinates in a typical LCH order. HCT is technically a LCh like space, but is ordered like HSL, and renames lightness to T for tone. So it breaks all the conventions. But I can test if it is an LCH type space, and just request coordinates in LCH order, or at least get the indexes in that order.

I think I declared the categories in my library as HSL-like, LCh like, Lab-like, RGB-like, HSV-like, HWB-like, and generic space. All spaces that are cylindrical are also generic cylinder spaces. So it is easy to check for hue carry forward if anything is cylindrical, but that doesn't mean you can grab lightness or saturation.

Personally, I prefer opt in, but if the goal is to provide a CSS experience out of the box, then opt out would make more sense.

@LeaVerou
Copy link
Member

LeaVerou commented May 25, 2024

One issue with Color.js is where would this logic live? Each color space until now is assumed to be independent. The only relation between color spaces is base, and it's one directional. Where would something like analogous components live?

The only thing I can think of right now is that we'd define a canonical set of coordinates (x, y, z, l, c, h, a, b) and coords will define which one they are analogous to if the name is different with a like descriptor. The like descriptor can also be specified to override the default assumption if the name is the same as one of these but they are not actually alike (e.g. the b in hwb).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
spec-parity Parity with the CSS Color specs
Projects
None yet
Development

No branches or pull requests

3 participants