Re: [community-group] [Format] Object vs Array (#55)

I believe it would useful if our format provided authors with a means to express that tokens in a group should be presented in a specific order. For example, if you have a bunch of tokens representing tints and shades of a colour then it's nice if they can be displayed in a specific order. Imagine a style guide generator that renders them as colour swatches.

Given that our basis is JSON, arrays feel like the most convenient way of achieving that. Doing that means that there are 2 flavours of groups:

* Object-based groups (whose tokens and nested groups do **not** have a defined order)
* Array-based groups (whose tokens and nested groups **do** have a defined order)

However, introducing array groups also brings a few challenges:

**Token names**
Right now, a token's name its key in the parent group. For example, to define a token called "my awesome token" you'd do something like this:
```jsonc
{
  "my awesome token": {
    "value": "#ff0000",
    "type": "color"
  }
}
```

But, if that's in an array, there is no (explicit) key:

```jsonc
[
  {
    // what is the name of this token??
    "value": "#ff0000",
    "type": "color"
  }
]
```

I had grappled with this a bit in my [UDT experiment](https://udt.design/), and landed on the following, which I propose as a potential solution for our spec:

* The token object itself can have an _optional_ `name` property to declare the token's name.
* If a token in an object has a `name` attribute, then that takes precedence over the parent object's key
* If a token in an array does _not_ have a `name` attribute, then the array index is used as its name

For example:

```jsonc
{
  "object group": {
    "token 1": {
      "value": 42
      // This token's name is: "token 1"
      // Its path is: "object group" / "token 1"
    }

    "token 2": {
       "name": "actual token name",
       "value": "hello world"
      // This token's name is: "actual token name" (note how the "token 2" key is now ignored)
      // Its path is: "object group" / "actual token name"
    }
  },

  "array group": [
    {
      "value": 99
      // This token's name is: "0" (since it has no name property, the array index is used instead)
      // Its path is: "object group" / "0"
    },

    {
      "value": 24,
      "name": "token name"
      // This token's name is: "token name"
      // Its path is: "object group" / "token name"
    }
  ]
}
```

_Aside:_ Another benefit of introducing the optional `name` property is that it could enable authors to name tokens with things that are reserved keywords in our spec. It's probably a very niche edge-case, but let's imagine you wanted to name your token "description". Normally, this wouldn't be possible because `description` is a reserved keyword used to add a text description to tokens and groups. _But_ you could work around it like so:

```jsonc
{
   "description": "This is the description of this group!",
   "ignored": {
      // This is a token whose name is "description"
      name: "description",
      value: 42
    }
}
```


**Group properties**
We currently allow groups to have their own `description` properties, and we've been discussing adding support more more. This works fine for the existing object-based groups where those are "special" keys (note to self: We really need to add a list of reserved keywords which cannot be used for token or group names to our spec!). But we can't do the same thing for an array group.

In my UDT experiments, I came up with the following potential solution. It's a bit clunky though...

* An array group can be written in 2 ways:
    * A plain JSON array, which directly contains the tokens and nested groups
    * A JSON object with a `tokens` property which is an array that contains the tokens and nested groups.
* When authoring an array group using the object syntax, group properties like `description` can be added to that object
    * No other properties may be added to the object though

Example:
```jsonc
{
  // This array group cannot be given a description or any other group property
  "array-style array group": [
     {
       // Token name: "red"
       // Token path: "array-style array group" / "red"
       "name": "red",
       "value": "#ff0000"
     }
  ],

  // But this one can have those things..
  "object-style array group": {
    "description": "The group's description",
    // This array group's actual tokens go in here...
    "tokens": {
       {
         // Token name: "green"
         // Token path: "object-style array group" / "green"
         "name": "green",
         "value": "#00ff00"
       }
    }
  }
}
```

A parser can distinguish between an object group, a token and an object-style array group as follows:

* Does the object have a `value` property? If yes, it must be a token. Otherwise...
* Does the object have a `tokens` property? If yes, it must be an object-style array group. Otherwise...
* It must be an object group

**Root level**
One of things we've discussed but not yet added to the spec is having some kind of property indicating the version of the DTCG spec that the file conforms to. It's conceivable there may be other file-level properties we need to add in future too (e.g. a list of other token files to import or include).

For sake of this point, let's imagine we decided to add the spec version by having a mandatory `$schema` property at the top level, whose value was a URL to a JSON schema corresponding the DTCG spec version. For example:

```
{
  "$schema": "https://designtokens.org/spec/v1/",
  // tokens go here...
}
```

That would require every DTCG token file to be an object at the top level (note, in plain JSON it's permissible for the top level to be any JSON value - array, string, number...). I think that by itsefl is fine and the current spec draft pretty much mandates that anyway.

_But_, if we decide to introduce array groups, then the consequences for that top-level group would be:

* **Either**, it can only be an object-style group, meaning that you cannot specify an order to the top-level groups and/or tokens in your file
* **Or**, it necessitates something like the object-style array groups I proposed in the previous section. That way, the top level of your file can be an array group (if it contains a `tokens` propert) or an object group otherwise.

My preference would be for the latter.

What do you think?

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


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

Received on Wednesday, 8 September 2021 08:59:38 UTC