Re: [community-group] Pre-defined composite type for refering color modifications (e.g. rgba) (#88)

From the Web development perspective as Design token users, the CSS Color 5 syntax and functions (like `color-mix`) feel natural, produce expectable results and would be easy to integrate in our workflow.

But with regards to this syntax there are a few questions open:


## 1. Where will the operations be defined?

[Currently](https://tr.designtokens.org/format/#color), values of the `color` type are defined in the draft like this:

> Represents a 24bit RGB or 24+8bit RGBA color in the sRGB color space. The $type property MUST be set to the string color. The value MUST be a string containing a hex triplet/quartet including the preceding # character.

As it is already discussed in #137, the goal is both supporting colors in other color spaces than sRGB but also limiting the scope of the v1 spec, which might be conflicting. I'd like to avoid moving the discussion from that issue over here, but would like to just mention that maybe the decision of _where_ we want to put the modifications might also influence the decision how we want to define colors in tokens themselves:

### 1.1. Do we need a new `$type` for relative colors?

If we'd use the relative color syntax as an actual _value_ of the token, then it would define clearly that this token has a modified value. For example:

```json5
{
  // Example base color
  "base": {
    "$type": "color",
    "$value": "#ff0000"
  },

  // Using the relative syntax to get the color to #7f0000
  "brand": {
    "$type": "color",
    // Note: Example syntax, it will be discussed in 2.1. further down
    "$value": "rgb(from {base} calc(r / 2) g b)"
  }
}
```

But this would break with the current definition of a color value, so maybe it might be preferred to use another type for this, e.g. `relative-color` or `color-relative`?

```json5
{
  // "base": { "$value": "#ff0000", ... } from above

  // Using the relative syntax to get the color to #7f0000
  "brand": {
    "$type": "relative-color",
    "$value": "rgb(from {base} calc(r / 2) g b)"
  }
}
```

This might also help Design tools to store relative colors differently than "regular" colors, but it might also cause some additional steps for them to just "switch" between these two steps. How can a Design tool abstract the complexity of defining colors in an easy way for users to just say: "I want to change this color to be 15% lighter than another base color?"


### 1.2. How do we compose multiple operations?

Of course, if we're adding a new `$type` for parsing relative colors, we would also need more types for other operations. As we also thinking of supporting the use case of `lighten`/`darken` a color (with `color-mix()`), we might need a special type for `color-mix` now, and then in the feature maybe even [CSS Color 6 `color-contrast`](https://drafts.csswg.org/css-color-6/#colorcontrast) to define an accessible text color for the given list of defined background colors.

If we want to combine multiple operations with regular CSS syntax, it might become quite hard to parse a single string. For example, if a user wants to lighten a color (which has 50% more green) by 10%, then it could look like this:

```json5
{
  // Example base colors
  "white": {
    "$type": "color",
    "$value": "#ffffff",
  },
  "darkgreen": {
    "$type": "color",
    "$value": "#007f00",
  },

  // Target color with example `color-mix` type
  "brand": {
    "$type": "color-mix",
    // Note: Example syntax, it will be discussed in 2.2. further down this comment
    "$value": "color-mix(in rgb, rgb(from {darkgreen} r calc(g + 0.5) b), {white} 10%)"
  }
}
```

Would it maybe help to reduce the parsing (and reading?) complexity by defining the modification as an `$operation` instead?

```json5
{
  // "white": { ... }, "darkgreen": { ... } from above

  // Target color with example `color-mix` type
  "brand": {
    "$type": "color",
    // Input value
    "$value": "{darkgreen}",
    "$operations": [
      // "$value" refers to original $value, as it is the first calculation
      // At this point it would be #007f00, the original value of {darkgreen} above
      {
        "color-relative": "rgb(from $value r calc(g + 0.5) b)",
      },
      // "$value" refers to current value after the previous calculation
      // At this point it would be #00ff00
      {
        // Note: Example syntax, it will be discussed in 2.2. further down
        "color-mix": "in rgb, $value, {white} 10%"
      }
    ]
  }
}
```

The `$value` would be a placeholder which referes the current value at each step of the operation. (It could of course also be written/defined in another way, this is just an example)

By using the `$operations` array, we can easily define a base color in the `$value` and do some calculations on it. On the other hand, since the `$value` is just another `color` type itself, it might be unintuitive to refer to `{brand}` in other tokens: Is it just the original `$value`, or the calculated value (in this case `#181818`)? And would it be possible to also refer to the original value on demand (e.g. via `{brand#value}` or `{brand.$value}`)? This also refers to issues #148 and #126.

Another benefit of an `$operations` array would be, if we could re-use them as separate tokens as described above:

```json5
{
  "lighten-soft": {
    "$type": "operation",
    "$value": [
      {
        "color-mix": "in rgb, $value, {white} 10%"
      }
    ]
  },
  "brand": {
    "$type": "color",
    "$value": "{darkgreen}",
    "$operations": [
      // Regular operation
      {
        "color-relative": "rgb(from $value r calc(g + 0.5) b)",
      },
      // Referenced operation
      "{lighten-soft}"
    ]
  },
}
```



## 2. Syntax

### 2.1. Variables

The CSS syntax uses CSS variables for referring to other variables in the color calculations. Would it be helpful to use the [Design token syntax for Aliases / references](https://tr.designtokens.org/format/#aliases-references) to refer to other Design tokens inside the file?

This would provide the benefit of keeping the same syntax for referring to other values within the same file, and also enabling CSS only users to refer to their actual values (if the translation tools would keep them "as is").

For example:

```json5
{
  "base": {
    "$type": "color",
    "$value": "#ff0000"
  },

  "brand": {
    "$type": "relative-color",
    // This would equal the same as the CSS definition...
    // rgb(from var(--base) calc(r / 2) g b)
    // ...which would resolve to...
    // rgb(from #ff0000 calc(r / 2) g b)
    "$value": "rgb(from {base} calc(r / 2) g b)"
  }
}
```

### 2.2. Function names

Depending on our decisions on composition and additional types, there would also be different options for defining the respective functions:

In 1.1., if we decide to just add modifications for the `color` type, then we would need to define the operations/functions in the value itself:

```json5
{
  "regular-color-example": {
    "$type": "color",
    "$value": "#007f00",
  },
  "relative-color-example": {
    "$type": "color",
    "$value": "rgb(from {regular-color-example} r calc(g * 2) b / 50%)"
  },
  "color-mix-example": {
    "$type": "color",
    "$value": "color-mix(in rgb, {relative-color-example} 50%, white 10%)"
  }
}
```

This format would be consistent, especially if we were for example deciding to define regular colors as a [CSS Color 4 `<absolute-color-function>`](https://drafts.csswg.org/css-color-4/#typedef-absolute-color-function) in #137:

```json5
{
  "css4-color-example": {
    "$type": "color",
    "$value": "rgb(0% 50% 0%)",
  }
}
```

On the other hand, if we decide to use the `$operations` array, maybe we don't need to define the color function, as it is already the name of the operation?

```json5
{
  "color-mix-operation-example": {
    "$type": "color",
    "$value": "{darkgreen}",
    "$operations": [
      // Should we just define the function arguments in the value...
      {
        "color-mix": "in rgb, $value, {white} 10%"
      },
      // ...or also repeat the function inside the value...
      {
        "color-mix": "color-mix(in rgb, $value, {white} 10%)"
      },
      // ...or use a generic operation name for color modifications?
      {
        "modify": "color-mix(in rgb, $value, {white} 10%)"
      },

      // The latter could then also be used for relative colors:
      {
        "modify": "rgb(from $value r g b / calc(alpha - 10%))"
      },
    ]
  }
}
```


## 3. Portability

Using this in the web would be fairly easy: A translation tool could just replace the Aliases/references with the actual calculated values and export a combinated token. A transformed result for the first example in 2.1. could be:

```css
:root {
  --regular-color-example: #007f00;
  --relative-color-example: rgb(from var(--regular-color-example) r calc(g * 2) b / 50%);
  --color-mix-example: color-mix(in rgb, var(--relative-color-example) 50%, white 10%);
}
```

The question would be how easy this could be ported to other platforms (as e.g. games, mobile, print). Would it be expected for a translation tool to be able to calculate the end result of a relative color or color-mix function?


## Considerations

From our perspective, having just a single `color` type which supports all of the regular CSS values would be the most consistent way to define colors. There's no need to wonder "Which type do I need?", "I need to mix different tokens together, how do I do it?", "What's the generated markup from the translation tools?".
On the other hand, declaring some generic operations as tokens themselves and being able to re-use them, would be quite a common use-case as well, so maybe a combination of both could provide a very flexible solution?

But of course all of this might increase the burden for implementers (such as Design tools and Translation tools). If we can reduce the complexity for them, it might also increase the adoption (as the specification might look less intimidating for new developers)...

-- 
GitHub Notification of comment by markuslangl
Please view or discuss this issue at https://github.com/design-tokens/community-group/issues/88#issuecomment-1217770544 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Wednesday, 17 August 2022 09:37:33 UTC