8000 [css-color-4] Clarify that `none` is preserved in calculations · Issue #10211 · w3c/csswg-drafts · GitHub 8000
[go: up one dir, main page]

Skip to content

[css-color-4] Clarify that none is preserved in calculations #10211

@LeaVerou

Description

@LeaVerou

(This came out of my comment here: #10151 (comment) and subsequent comments)

The spec is currently unclear about this, though @svgeesus made the case that this is editorial. @romainmenke thinks it's a substantive change. Regardless, we need to fix it ASAP to avoid web compat roadblocks.

Currently, css-color-4 is a little unclear on what happens with none values in authorland calculations, and implementations are currently converting none to 0 if used in calc() and other math functions, which comes up a lot in Relative Color Syntax. This was never our intent, the only reason converting none to 0 exists is that we don't want to be exposing color space conversion math, and/or sometimes you literally need to actually display a color that includes none components so you need to do something.

Even when converting to different color spaces, the spec already includes the concept of analogous components, to minimize none0 conversions, and in #10210 I proposed expanding it a bit.

Note that while none was originally conceived to express achromatic colors and the chroma of white & black, it is actually useful way beyond that, as it allows expressing parameterizable colors, in a way that decouples the calculation from the color (unlike RCS which requires them both at the same time). You only specify the bits that don't change (e.g. hue), leave the rest none, and let normal CSS operations take their course. E.g. interpolating any (polar) color with oklch(calc(none - 0.4) none none) interpolates with a darker version of that color. Sure, you can do all these things with pure RCS, but this decouples the parameter from the modification, so you don't even need to know what you’re interpolating with, it just works.

Converting to 0 if used in calculations serves no purpose other than simplifying implementations, reduces none’s usefulness in creating dynamic colors that can be passed around, and introduces several problems:

  • Discontinuity since e.g. h and calc(h) are not the same,
  • It makes browser interpolation magic compared to what authors can do with calc() or even calc-mix() + RCS.
  • Disregards author intent

I propose we introduce the concept of none-containing component and clarify that:

  • Calculations preserve none. Meaning, calc(h + 20) in RCS would become calc(none + 20). If h is already an expression containing none, it can be simplified, but only in ways that do not alter its meaning. E.g. if h is calc(none - 10), calc(h + 20) can become calc(none + 10) but can also just stay calc(calc(h - 10) + 20) or calc(h - 10 + 20).
  • Interpolation operations (color-mix(), calc-mix(), gradients etc) resolve none to the other component if the other component is not none-containing. If the other component is none-containing, they resolve to a calc-mix() expression containing both values. E.g. interpolating between a chroma of none and a chroma of clamp(.1, none, .2) at 50% would produce calc-mix(50%, none, clamp(.1, none, .2)). If this color is later interpolated with a color that has a chroma of 0.15, the nones would become 0.15, so the component would become calc-mix(50%, 0.15, clamp(.1, 0.15, .2)) = 0.15.
  • Authors should also be able to specify things like clamp(50, none, 70) directly.

One thing we need to sort out is what is the precedence is when a function also accepts none is used in color components. E.g. we recently resolved to allow none for the upper and lower bound of clamp(). In those cases, what does none mean when used in a color component? I would vote for giving precedence to the color-related meaning of none since that serves a unique purpose, whereas none in other places is is simply syntactic sugar.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0