Open jorenbroekema opened 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.
@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
I wouldn't restrict the proposed Array types to 1, 2, 3, 4 items for borders/dimensions
Agreed! Maybe we can have:
Dimension
-> Dimensions
The plural gives a simple rule and an intel on the array-based value.type Dimensions = Array<Dimension>
type Borders = Array<Border>
...
Paddings
, Margins
...
Usage-based tokens would convey more context to speed up the UI integration and code generation.
Yet it's definitely more challenging to land on specs that would fit most, if not all, the platforms. Probably discussions for later versions of the spec.type Paddings = [Dimension] | [Dimension, Dimension] …
type SquareBorders = [Border] | [Border, Border] …
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"