Csswg-drafts: "Can't be displayed" is undefined

Created on 5 May 2020  路  20Comments  路  Source: w3c/csswg-drafts

Originally posted over here.

In section the color function

The color function takes one or more comma-separated arguments, with each argument specifying a color, and later colors acting as "fallback" if an earlier color can鈥檛 be displayed (for example, if the colorspace it specifies hasn鈥檛 been loaded yet).

can't be displayed is undefined except for an illustrative, parenthetical example.

Closed Accepted by Editor Discretion Needs Edits css-color-4

Most helpful comment

So I'm tending towards:

  1. Defining _invalid_ (incorrect syntax, unfetched profile, and so on)
  2. Defining _out of gamut_
  3. Making _can't be displayed_ the union of _invalid_ and _out of gamut_

This means that operations such as color mixing can still be performed, if desired, on out of gamut colors, while making the color() fallback more useful

All 20 comments

Not clear whether can't be displayed and invalid color are intended to be identical.

The color() function represents the color specified by the first of its arguments that represent a valid color
(that is, the first argument that isn鈥檛 an invalid color). If all of its arguments represent invalid colors, color() represents opaque black.

invalid color links to itself and is not actually defined, either :)

I think it鈥檚 far more useful if the fallback is used for out of gamut colors as well.
Now another thing to decide is what happens when the color refers to an invalid color space (one that has not loaded yet, or not specified at all) if there鈥檚 no fallback. Does it get treated as an invalid value and cascade?

what happens when the color refers to an invalid color space (one that has not loaded yet, or not specified at all) if there鈥檚 no fallback. Does it get treated as an invalid value and cascade?

The spec says:

If all of its arguments represent invalid colors, color() represents opaque black.

so it has a resolved value and does not cascade.

So I'm tending towards:

  1. Defining _invalid_ (incorrect syntax, unfetched profile, and so on)
  2. Defining _out of gamut_
  3. Making _can't be displayed_ the union of _invalid_ and _out of gamut_

This means that operations such as color mixing can still be performed, if desired, on out of gamut colors, while making the color() fallback more useful

Oh no. I've just realised my brain didn't fully engage with my final comment on #5045.

  • color(foo 0 0 1) is in gamut because all the components are in the range 0..1
  • color(foo 0 0 2) is out of gamut because one of the components is not in the range 0..1

For some reason I was imagining maths here, but of course testing whether a color is in the gamut of _it's own colorspace_ is just a trivial test to see if all components are in the range 0..1. Deciding whether a color() with a component value of "2" is invalid, or valid-but-out-of-gamut is largely academic, as it has the same result. Sorry, not sure how I missed that!

Chris, in #5045 you also mentioned that ensuring TAC (total area coverage) does not exceed the maximum for an additive colorspace would also be part of the in-gamut test. This might be more problematic - first because I don't believe the TAC limit is stored in the ICC profile, second because "registration black", where all the inks are at 100% (e.g. color(fogra39 1 1 1 1)) is sometimes used in print (for printer marks in the page margins). If the color() function consider this out-of-gamut and fell back to the alternative color, it would be a problem.

Indeed, I remember using black with all inks at 100% quite a few times when working with print. In general, I think involving TAC in this is unlikely to be helpful. The color is still in gamut after all, whether the paper survives it or not :)

Oof, yeah, I really didn't define that well enough. I think I didn't intend for out-of-gamut to do anything special here; the fallback was just for color-spaces that haven't loaded and such.

That said, it sounds reasonable to me to make it also mean "not in the output gamut".

If we do that, tho, we still want to distinguish between "out of gamut" and straight up "invalid"; we don't want to resolve color() to transparent black just because the last color in its list was out-of-gamut! Instead we want to try and render that color, using the existing gamut-correction stuff that we'd do with anything else.

So yeah, the logic then becomes:

  • if the specified color is decodeable and in-gamut, use it.
  • if the specified color is decodeable and out-of-gamut, attempt to use the fallback. If the fallback is an invalid color, instead use the specified color anyway.
  • if the specified color isn't decodeable, use the fallback. (If the fallback ends up being invalid too, that's fine, it'll just make the outer function an invalid color too.)

@faceless thanks for your comments, I left TAC out of the definition.

@svgeesus, I don't see how the commit does what you want. The specific bit of text describing the fallback is

The ''color()'' function represents the color
specified by the first of its arguments that represent a valid color

Which doesn't invoke anything about out-of-gamut colors. "can't be displayed" is only referenced in the intro describing the function.

After discussing this with @svgeesus, it seems that there's a bunch of things we need to decide about color() fallbacks and validity before we serialize to spec prose.

  • If a color() specifies an invalid color but its fallback is valid, is the color() call valid? It seems like it should be, as this is how var() fallbacks work, but we need to resolve.
  • It is currently specified that if a color space is not supported, the color is invalid. However, if the color space is a <dashed-ident>, the color should be invalid at computed value time, since the color space may always be loaded later. Keyword-defined color spaces can be invalid at parse time, which means their fallbacks can be provided via the cascade.
  • If the <dashed-ident> color space is available, but the arguments are incorrect, the color should still be invalid at computed value time, because the @color-profile rule can always be removed and replaced with a different one, for which the arguments are valid. Contrary, for a keyword color space, the validity of the arguments is known at parse time.
  • If the fallback applies both when the (<dashed-ident>) color space is not available and when the color is out of the screen gamut, how do you reasonably provide separate fallbacks for these two cases? I imagine the fallback one may want to provide could be significantly different for manual gamut mapping or for an undefined color space. For example, if the color space is unavailable, the fallback must be in a different color space. Whereas, if the color space is available but the color is out of gamut, the fallback could still be in the same color space, just with different arguments.
  • Similarly, if the fallback is applied in both cases, how does one specify that if the color is OOG they want to use the browser's built-in gamut mapping, but still provide a fallback if the color space is not available? Would it be useful to have the ability to specify separate fallbacks?
  • I wonder if it would be best to provide a media query or other type of conditional to check if a color space is available?
  • How does one provide a fallback to lch()? It's not available as a predefined color space on color(), and the lch() function doesn't have a fallback parameter. I suppose it's possible via color(--nonexistent, lch(...), fallback) but that's clunky (and depends on having multiple fallbacks, which I'm not convinced we need). Then again, perhaps the in-gamut MQ can resolve these cases.

If a color() specifies an invalid color but its fallback is valid, is the color() call valid? It seems like it should be, as this is how var() fallbacks work, but we need to resolve.

Yes, otherwise fallback doesn't mean anything.

It is currently specified that if a color space is not supported, the color is invalid. However, if the color space is a , the color should be invalid at computed value time, since the color space may always be loaded later. Keyword-defined color spaces can be invalid at parse time, which means their fallbacks can be provided via the cascade.

Ah, there's some confusion here. "Invalid color" has nothing do with parsing validity, and thus doesn't touch on notions of IACVT at all. color(--unknown) will just render as transparent black, not trigger anything related to variables. Whenever a @color-space is changed/added/removed, any color() functions referencing it just get re-checked and might change the color they represent.

color(unknown) would indeed be invalid at parse-time, as normal, because it doesn't match the grammar.

(If the spec currently says that an unknown dashed-ident makes the function invalid, that's no good and needs fixing.)

If the color space is available, but the arguments are incorrect, the color should still be invalid at computed value time, because the @color-profile rule can always be removed and replaced with a different one, for which the arguments are valid. Contrary, for a keyword color space, the validity of the arguments is known at parse time.

Yup.

If the fallback applies both when the () color space is not available and when the color is out of the screen gamut, how do you reasonably provide separate fallbacks for these two cases? I imagine the fallback one may want to provide could be significantly different for manual gamut mapping or for an undefined color space. For example, if the color space is unavailable, the fallback must be in a different color space. Whereas, if the color space is available but the color is out of gamut, the fallback could still be in the same color space, just with different arguments.

Ooh, good point. That suggests we might want to limit ourselves to the "color-space not yet loaded" case, and leave gamut-handling to something else (addressed below). That simplifies the fallback logic significantly, as well.

I wonder if it would be best to provide a media query or other type of conditional to check if a color space is available?

Yes, the "in-gamut" MQ case is being discussed in #5045; I think it's a great idea.

We could also address this inline with something like an in-gamut() function, which takes 1+ colors and selects the first one in the device's output gamut, auto-forcing the last one into gamut if necessary. (And maybe allowing a mapping strategy to be selected for that last one? Then it's useful even for a single argument.)

How does one provide a fallback to lch()? It's not available as a predefined color space on color(), and the lch() function doesn't have a fallback parameter. I suppose it's possible via color(--nonexistent, lch(...), fallback) but that's clunky (and depends on having multiple fallbacks, which I'm not convinced we need). Then again, perhaps the in-gamut MQ can resolve these cases.

This supports the argument that gamut fallback should be handled separately from "undefined colorspace" fallback.

Yes, the "in-gamut" MQ case is being discussed in #5045; I think it's a great idea.

Thank you, I'm glad you think it's a great idea! However, what I was discussing in the quote was a MQ to check if a color space is available, i.e. something like @media (color-space-exists: --forgra39).

Oh, sorry for misreading!

Hm, I'm not sure about the value of that. Custom colorspaces are entirely under the control of the author already; it's not unknown device/media information. An author knows whether a colorspace exists by virtue of checking whether their code puts that @colorspace rule in the stylesheet or not.

color(unknown) would indeed be invalid at parse-time, as normal, because it doesn't match the grammar.

Ah, nm, I forgot that the grammar is just a generic <ident> there, precisely to avoid new colorspaces making the entire function invalid (so you can use fallback instead). So that's valid, and would also render as a transparent black.

(If the spec currently says that an unknown dashed-ident makes the function invalid, that's no good and needs fixing.)

It currently says:

If the <dashed-ident> names a non-existent colorspace ( a name that does not match an color profile鈥檚 name, or which matches but the corresponding profile has not loaded, or does not represent a valid profile), this argument represents an invalid color.

What is wrong with that? It doesn't make the entire function invalid, but it makes that argument invalid (so you move on to the fallback arguments)

I wonder if it would be best to provide a media query or other type of conditional to check if a color space is available?

Yes, the "in-gamut" MQ case is being discussed in #5045; I think it's a great idea.

It is a great idea, and is related to, but distinct from, finding whether a given color space exists.

For example, consider

@color-profile --myHugeProfile {
  src: url('https://example.org/Megabytes.icc');
}
.blue_sky {
  background-color: 
  color(--myHugeProfile 0.572088 0.229346 0.081708 0.282044 0.000000 0.000000 0.168260);
}

This would be a conditional to see if I can use this colorspace or not (has it loaded yet).

That suggests we might want to limit ourselves to the "color-space not yet loaded" case, and leave gamut-handling to something else (addressed below). That simplifies the fallback logic significantly, as well.

It was a concern over tying out-of-output-device-gamut in as part of cannot-be-displayed that led to @LeaVerou and I discussing this yesterday (and her write-up of the issues we discussed).

My edits today and yesterday have been adding the out of gamut stuff, to see how it looks. But I am certainly open to splitting the two concepts again and handling them differently.

I want it to be possible for an author to specify a preferred fallback to a given color; I also want good things to happen if the author does not do that.

What is wrong with that? It doesn't make the entire function invalid, but it makes that argument invalid (so you move on to the fallback arguments)

Yup, that's just fine. Lea was saying something else that I wanted to verify didn't happen.

I have been @ summoned, what is this about? XD

Nothing, your GitHub username is just the same as a (misspelled by me) CSS at-rule. (It's actually @color-profile ^_^ https://drafts.csswg.org/css-color-4/#at-profile)

Was this page helpful?
0 / 5 - 0 ratings