# Specify Design Token Format

### Introduction

The Specify Design Token Format (SDTF) is a JSON based notation made to capture the architecture of information of design tokens regardless of its source. Supporting 50+ token types, the SDTF allows to compose the most complex types by referencing/aliasing others. Its primary focus is compatibility among design (system) tools and the frontend technologies used to build design systems.

**In Specify, each repository is home to one SDTF token tree.**

{% hint style="warning" %}
The SDTF is a transport format, if it can be read easily, we do not recommend writing its content directly. Instead, use the APIs provided by Specify.
{% endhint %}

## JSON Tree Structure

With the SDTF, you organize design tokens as a regular JSON tree with some remarkable signatures such as **token**, **group** and **collection**.

With groups and collections, you create sets of tokens to convey meaning like `color.primary` where `color` is the group and where my `primary` is a token, nested in `color`.

There is no technical limitation to the level of nesting you can achieve, yet good practices would drive the usage to a couple or so.

<details>

<summary>Example with dimensions in a group</summary>

```typescript
const dimensionTokens: SpecifyDesignTokenFormat = {
  spacing: { // spacing is a Group
    one: { // one is a Token
      $type: 'spacing',
      $value: {
        default: { unit: 'px', value: 4 }
      }
    },
    two: { // two is a Token
      $type: 'spacing',
      $value: {
        default: { unit: 'px', value: 8 }
      }
    }
  }
}
```

</details>

<details>

<summary>Example with colors in a collection</summary>

```typescript
const tokens: SpecifyDesignTokenFormat = {
  theme: {
    $collection: { $modes: ['light', 'dark'] },
    color: {
      blue: {
        $type: 'color',
        $value: {
          light: { model: 'hex', hex: '#1919C4', alpha: 1 },
          dark: { model: 'hex', hex: '#111146', alpha: 1 }
        }
      }
    }
  }
}
```

</details>

## Tree node (token, group, collection)

A tree node is any of **token**, **group** or **collection** that live within a SDTF token tree.

### Naming

Token, group and collection names are free to use any character except the following:

* **`.`** (dot/period) used for aliasing
* **`$`** reserved for SDTF

### Common properties

Any group, collection or design token can hold extra properties:

* `$description` a string aimed to complement the name.
* `$extensions` a JSON object to complement the value for a given provider.

<details>

<summary>Example with dimensions</summary>

```typescript
const dimensionTokens: SpecifyDesignTokenFormat = {
  spacing: {
    $description: "Spacings for the webapp",
    one: {
      $description: "default spacing",
      $type: "spacing",
      $value: {
        default: { unit: "px", value: 4 },
      },
    },
    two: {
      $description: "doubled spacing",
      $type: "spacing",
      $value: {
        default: { unit: "px", value: 8 },
      },
      $extensions: { "com.specifyapp.highlighted": true },
    },
  },
};
```

</details>

#### Note on `$extensions`

The key used to index the `$extensions` object in `$extensions: { [key: string]: JSONValue }`MUST follow the [reverse domain name notation](https://en.wikipedia.org/wiki/Reverse_domain_name_notation) in order to ensure collision-free collaboration of actors.

### Tree path

The tree path is the **unique identifier** of a tree node within the token tree. It is formed from the enumeration of the node's parent names.

In the following example, the token `base` is nested into the `spacing` group. Thus, its path is `['spacing', 'base']` or `"spacing.base".`

```typescript
const dimensionTokens: SpecifyDesignTokenFormat = {
  spacing: {
    base: {
      $type: "spacing",
      $value: { default: { unit: "px", value: 4 }},
    },
  },
};
```

#### Representations

Among the Specify APIs, you'll find 2 path representations.

* the **`string`** based representation concatenates the path with `.` (dot/period) . Mainly used for aliasing. Giving for our previous example `"spacing.base"`.
* the **`array`** based representation with an array of string, where each item is the name of a tree node. Mainly used for working with the SDTF Engine. Giving for our previous example `['spacing', 'base']`.

## Design token

A design token represents the value of a design decision for all the possible modes you want to attach. It is defined by its `$type` and its corresponding `$value`, where the `$value` is a JSON object, `Record<mode: string, value: any>`, where the mode name is the key and holds to the actual value.

A design token can be placed anywhere within a group or a collection.

<pre class="language-typescript"><code class="lang-typescript"><strong>const dimensionToken = {
</strong>  $type: 'dimension', // Mandatory
  $value: { // Mandatory
    default: { // Stands for "default mode", mode name can be any string not starting with "$"
      value: 1, unit: '%' // Must be a valid value or reference for the type
    }
  },
  $description: 'A dimension token', // Optional string
  $extensions: { 'com.specifyapp.data': true } // Optional Record&#x3C;string, JSONValue>
}
</code></pre>

### Token value

#### Value defines at least one mode

Any Token Value MUST define at least one mode.

Default mode, by convention, is: `default` such as `$value: { default: ... }`

#### Modes naming

Modes names are free to use any character except the following:

* **`.`** (dot/period) used for aliasing
* **`$`** reserved for SDTF

#### Modes outside a collection

A token placed outside of a collection can define any mode as far as it defines at least one mode.

#### Modes inside a collection

A token placed inside of a collection must **strictly define the modes allowed by the collection**.

### Aliasing (reference)

Within a design token value, we can declare `{ $alias: 'path.to.token' }` to reference another token, thus avoiding information duplication.

You can alias tokens through three levels.

#### Top-level aliasing

You can alias an entire Token. Hence you also inherit from its modes.

```typescript
aDimension: {
  $type: 'dimension',
  $value: { $alias: 'a.reference.to.a.dimension' },
}
```

*Note that `$mode` does not apply here.*

#### Mode-level aliasing

You can alias on one or many modes of a Token. The value resolution will follow the indicated target `$mode`.

```typescript
aDimension: {
  $type: 'dimension',
  $value: {
    small: { $alias: 'a.reference.to.a.small.dimension', $mode: 'customMode' }, // this is the mode level alias
    large: { value: 1, unit: 'px' },
  },
}
```

*Note that `$mode` is mandatory*

#### Value-level aliasing

You can alias on (almost) all sub-values composing the SDTF. The value resolution will follow the indicated target `$mode`.

```typescript
aDimension: {
  $type: 'dimension',
  $value: {
    default: {
      value: { $alias: 'a.reference.to.a.number', $mode: 'small' },
      unit: { $alias: 'a.reference.to.a.dimensionUnit', $mode: 'web' }
    },
  },
}
```

*Note that `$mode` is mandatory*

#### Aliasing validation

Aliases must reference compatible Tokens. Each type has a unique mapping to other types defining which ones can be referenced at which place.

<details>

<summary>Example of invalid aliasing</summary>

Here is a dimension using value level aliasing.

```tsx
aDimension: {
  $type: 'dimension',
  $value: {
    default: {
      value: { $alias: 'a.value', $mode: "default" },
      unit: { $alias: 'a.unit', $mode: "default" },
    },
  },
},
```

1. The Token expected to resolve for `{ $alias: 'a.value' }` must be a `number`.
2. But since `number` is a broad definition, we would also accept `integerNumber, zeroToOneNumber, arcDegreeNumber, rgbColorNumber, positiveNumber, positiveIntegerNumber`.
3. Following the rationale, the Token expected to resolve for `{ $alias: 'a.unit' }` must be a `dimensionUnit`.

</details>

### Supported types

The SDTF defines a large variety of token types in order to capture (almost) any design decision.

* bitmap
* blur
* border
* breakpoint
* color
* dimension
* duration
* font
* gradient
* gradients
* opacity
* radius
* radii
* shadow
* shadows
* spacing
* spacings
* textStyle
* transition
* vector
* zIndex

<details>

<summary>UI type helpers</summary>

* bitmapFormat
* borderStyle
* borderStyleLineCap
* cubicBezier
* dimensionUnit
* durationUnit
* fontFamily
* fontFeature
* fontFeatures
* fontFormat
* fontStyle
* fontWeight
* shadowType
* stepsTimingFunction
* textAlignHorizontal
* textAlignVertical
* textDecoration
* textTransform
* vectorFormat

</details>

<details>

<summary>JSON inherited types</summary>

* string
* number
* boolean
* null
* array
* object

</details>

<details>

<summary>Constrained primitives</summary>

* integerNumber
* zeroToOneNumber
* arcDegreeNumber
* rgbColorNumber
* positiveNumber
* positiveIntegerNumber
* percentageNumber
* hexadecimalColorString

</details>

## Group

A group is meant to order and nest design tokens semantically, it can be placed in any parent group or collection.

A group is a simple JSON object that can contain none to many groups, collections or tokens. With no mandatory keys or children, a group can be empty and represented as an empty object `{}`.

<pre class="language-typescript"><code class="lang-typescript"><strong>const tokens = {
</strong>  spacing: { // Group name
    $description: 'All spacings for webapp', // Optional string
    $extensions: { 'com.specifyapp.data': true } // Optional Record&#x3C;string, JSONValue>
    base: {
      // ... token data
    }
  }
}
</code></pre>

## Collection

A collection is a special kind of group defining a mandatory `$collection` property. It constrain any enclosed token to respect a given set of modes.

A Collection can be placed within any group, nested at any level. But a Collection **cannot be nested within another Collection**.

```typescript
color: {
  $collection: { $modes: ['light', 'dark'] }, // $collection indicates this group is a collection
  black: {
    $type: 'color',
    $value: {
      light: { model: 'hex', hex: '#000000', alpha: 1 },
      dark: { model: 'hex', hex: '#000000', alpha: 1 },
    }
  },
  aGroup: { $description: 'This group is empty but shows it is allowed here' }
}
```

### `$collection` property

The `$collection` property is an object strictly containing the `$modes` key.

The `$modes` property is an array of mode names (string) defining the allowed modes for any token within the collection.
