[csswg-drafts] [css-color-4] Color modifications proposal: extending color functions

LeaVerou has just created a new issue for https://github.com/w3c/csswg-drafts:

== [css-color-4] Color modifications proposal: extending color functions ==
The lack of a color modification syntax is one of the few things that authors still use preprocessors for, and severely limits what CSS variables can do. I'm going to post this proposal I've been thinking about for a while. It's by no means perfect, but perhaps it can get the discussion going, so we end up with something decent eventually!

### Goals of a color modification syntax

- Setting any color coordinates for all color functions, either to absolute values or to values relative to the modified color (e.g. set to 0.3 or increase by 50%)
- Avoiding accidental gamut clipping. If a syntax converts to sRGB, it should be syntactically obvious that this is happening.
- Easy lighter/darker variants (tint/shade)
- Blending of two or more colors
- Getting white or black depending on which one contrasts better with the base color

### Proposal

This is loosely inspired from the current practice of defining major theme colors as color components in a variable (e.g. `--color: 180, 60%`) and then using them in color functions (e.g. `hsl(var(--color), 90%)`). However, this is simple textual substitution, which is insufficient, and imposes constraints on how the base color is defined.

I’m proposing to extend all color functions to allow for a `<color>` argument. If the color argument is present, all other arguments become optional, and are filled from the color argument. For example, if a function’s grammar is `<number>{3}`, it will become `<number><color> {0,3} | <number>{3}`.

For example, `hsl(red 90%)` would create an HSL color with a lightness of 90%, and the saturation and hue from the provided argument. **Potential flaw:** In `hsl(red 0% 50%)`: is the `0%` saturation or lightness? We need to parse the next token to figure that out. However, since the lookahead is bounded, I didn't consider it a significant problem. If it is, we could have the color be last, which makes it slightly harder to read for humans, but every token is unambiguous from the moment of parsing.

Before any modification, the color would be converted to the target color space of the function used. This means that modifications using `hsl()` can be lossy (since the color would need to be converted to sRGB), but there is no such problem with `lab()` or `lch()`. However, any syntax that allows modifying HSL or RGB coordinates would have the same issue, at least this syntax makes the conversion more explicit.

**Backfilling arguments is not sufficient for most modifications**, and cannot perform relative modifications (e.g. "increase L by 10%" instead of "set L to .3"), which are far more common use cases. Therefore, we need to introduce a syntax for relative modifications as well. 

Ideas for that:
- `rel(<percentage>)`: Multiplies the color component by `<percentage>`. E.g. `lab(red rel(60%) rel(100%) rel(100%))` would convert `red` to Lab, and then multiply its L by 60%.
- `up <percentage>`/`down <percentage>`: Relative addition/subtraction. The previous example would be `lab(red down 40% up 0% up 0%)`. Given the lack of commas that we recently introduced, I'm finding this hard to read, since the keywords are not visually grouped with their percentages.
- Defining keywords for each coordinate, then using `calc()`. E.g. the previous example would be `lab(red calc(.4 * l) a b)`. Seems the most expressive and flexible, but probably too messy to define.
- Perhaps a special keyword to indicate _no modification_, e.g. `same`, would improve readability of the first two ideas.

With the syntax for relative modifications, this addresses most desired adjustments, and expands naturally with every new color function. Tint and shade could be relative modifications on `hwb()`, though adjusting the L of Lab/LCH is better as it's lossless (and produces fantastic results).

### Benefits of this syntax

- Extends on syntax that authors are already familiar with. No need to learn different ways to refer to the same color components.
- Can be understood by anyone who understands the existing color functions, even if they are unaware that a modification syntax exists.
- Makes the target color space very obvious, since it looks like creating a new color
- Every new color function comes with new color adjusters for free, no need to learn anything new besides using the color function.

### Drawbacks

- Does not address contrasting colors
- Does not address blending of two or more colors. Perhaps these could be defined as separate functions?
- It’s easy to clip the modified color to sRGB, since authors are very familiar with HSL and many would use that. However, any color adjustment syntax that supports adjusting hue, saturation, lightness would have the same issue. At least this syntax makes it obvious that you are creating an `hsl()` color, with the gamut limitations that this comes with.
- Stupid lossy transformations can be defined, like `color(hsl(color(mycmyk .1 .2 .3 .4) mycmyk 60%) .4)`. However, a) any sufficiently powerful syntax will allow for silly things, and b) see point above.

Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/3187 using your GitHub account

Received on Monday, 1 October 2018 18:27:49 UTC