design-tokens / community-group

This is the official DTCG repository for the design tokens specification.
https://tr.designtokens.org
Other
1.56k stars 63 forks source link

Multiple values - Array type #239

Open jorenbroekema opened 3 months ago

jorenbroekema commented 3 months ago

Raising this issue to discuss multi-value design tokens.

Relevant because I saw this idea pop up first for Shadow tokens, as it's quite common to layer shadows on top of another: https://github.com/design-tokens/community-group/issues/100

Another example is dimension tokens e.g. when defining the padding of an element, say a button. You may want to specify a vertical and horizontal padding (or even all 4 sides explicitly), just like you may want to specify multiple shadow values as a single shadow. I think in both scenarios they can be viewed as a single design decision, thus is makes sense for it to be a single design token. There's probably other examples we can think of, e.g. multiple gradient tokens being stacked at different coords to create gradient meshes/blobs. Transitions is also a good one, since those can be stacked as well to create a multi-step or multi-layered animation.

This brings up the broader question, should all tokens allow you to use an Array as value, where each item must adhere to the type specification? Is "multi-value" valid for every token? Is it only valid for some tokens, and if so, for which ones is it not and why not? This could obviously be misused to squish many design decisions into a single token, which is why I'm not completely confident to have a strong opinion on the answer to this and why I think it warrants discussion.

I am leaning towards saying "yes, it's valid for all tokens, and the spec should guide users on what would be considered misuse of the feature"

nclsndr commented 3 months ago

That's a topic we discussed a lot while building the Specify format.

My 2 cents: Array based values are necessary to express specific decisions, best is to use specific names for that matter, otherwise aliasing becomes a nightmare and users get lost.

I think, the best way to host array based value is by creating semantically meaningful types like: gradients, shadows, spacings, colorList (as proposed in #228)... Doing so, we make sure we give users a type they can read about, and we can provide them with a clear documentation on how to use it.

On the aliasing side, let's make a simple thought experiment: we have a color token with an array of 2 values. This color token is referenced within the color property of a border token. Which color is the border going to use? So simple (and singular) types should host one value only.

As a starting point I would propose the following types:

color

type ColorList = Array<Color>

dimension

type Spacings = 
  | [Dimension]
  | [Dimension, Dimension] 
  | [Dimension, Dimension, Dimension] 
  | [Dimension, Dimension, Dimension, Dimension]

number

type NumberRange = [Number, Number, Number] // min, max, step
type NumberList = Array<Number>

border

type Borders = 
  | [Border]
  | [Border, Border] 
  | [Border, Border, Border] 
  | [Border, Border, Border, Border]

shadow Related to #100

type Shadows = Array<Shadow>

gradient

type Gradients = Array<Gradient>

I can't think of related types for: fontFamily, fontWeight, duration, cubicBezier, strokeStyle, transition, typography.

Here how the aliasing might work:

{
  "simpleBorder": {
    "$type": "border",
    "$value": {
      "color": "#000000",
      "width": "1px",
      "style": "solid"
    }
  },
  "multiBorder": {
    "$type": "borders",
    "$value": [
      "{simpleBorder}",
      {
        "color": "#FF0000",
        "width": "2px",
        "style": "dashed"
      }
    ]
  },
  "cardBorder": {
    "$type": "borders",
    "$value": "{multiBorder}"
  }
}

Leveraging both nested and semantic aliasing while keeping clear the role of each token.

jorenbroekema commented 2 months ago

@nclsndr My only addition to your comment is that I wouldn't restrict the proposed Array types to 1, 2, 3, 4 items for borders/dimensions or 3 values for color ranges. While for most color spaces there are 3 channels and the (CSS) box model uses 4 sides, who's to say this will always be the case? Maybe in other platforms there's more parameters and they don't share the same box model. Imo easier to simplify by just allowing any number of items

nclsndr commented 2 months ago

I wouldn't restrict the proposed Array types to 1, 2, 3, 4 items for borders/dimensions

Agreed! Maybe we can have:

type Dimensions = Array<Dimension>
type Borders = Array<Border>
...
type Paddings = [Dimension] | [Dimension, Dimension] …
type SquareBorders = [Border] | [Border, Border] …