stylehub.cloud

Design tokens, in thirteen formats

On this page

Every color, typeface, size, breakpoint, duration, radius, shadow and theme in your manual is also a file a build can read: 13 formats, two archives, and an API address that serves the same values.

What a design token is here

WhereDashboard, Export tokens

A token is a name and a value, taken from a block you already filled in. You enter nothing twice for the export. Prose, rules, do and don't pairs and images are not tokens. The page and its files need the permission View the brand manual, which every built in role has, and no plan holds the export back.

Which manual the page reads

The page reads the brand's primary manual, every section of it, published or not. So what a reader sees and what you download can differ while you are still working. For any other manual, read its tokens over the API at its own address. See Design tokens in the API (opens in a new tab).

Which block fills which group

Nine blocks produce tokens. Each fills one group, and the group name is the folder name in the archive.

BlockGroupWhat it produces
ColorcolorsOne token per swatch, plus the eleven tints and shades when the block shows them.
TypographytypographyOne token per typeface, named after its usage.
Type scaletype-scaleEvery step: a size, a line height, and a tracking when it is not zero.
Spacing scalespacingEvery step of the scale, as a length.
Grid and layoutbreakpointsThe named breakpoints. Columns, gutters and margins are not tokens.
MotionmotionThe durations and the easing curves, as two groups.
Corner radiusradiusEach named radius as CSS. A full radius is written as 9999px.
ElevationelevationEach level as a box-shadow value.
ThemesthemesOne group per theme, holding the role colors that theme sets.

Every other block reaches brand-manual.md in the brand kit and the manual's JSON over the API, and no token file.

When a manual has several blocks of one kind

Color, Typography, Grid and layout, Motion, Corner radius, Elevation and Themes are collected from every block of that kind, in section order: two Color blocks make one palette. Spacing scales are merged and repeats dropped. The type scale is the exception: the first Type scale block wins and a second does not replace it.

How a value becomes a named token

A token name is the label lowercased, with anything that is not a letter or a digit turned into a hyphen and the hyphens trimmed off both ends. Deep sea 40 becomes deep-sea-40.

No name, or the same name twice
A swatch with no name takes its position: color-1, color-2. A repeated name is numbered within its group, so the second is primary-2. Same for typefaces, breakpoints, durations, easings and themes.
Typefaces
Named after the usage: a heading font is font-heading and the family name is the value. No usage gives font, then font-2. No family name means no token.
Spacing steps
Filed under the number, a half step keeping its point as a hyphen: 2.5 becomes spacing-2-5, and 2_5 in the code formats. Bare digits get px; a step with a unit is left as it is.
Themes
A group named after the theme, holding one token per role. The roles are a fixed list: background, surface, text, text muted, primary and border.

What is left out

A color whose hex cannot be read is in no token file and in no swatch group. A breakpoint, duration or easing with an empty value is not a token. An empty theme role is dropped, and a theme with no readable color is not exported at all.

The page itself

WhereDashboard, Export tokens

Eight tiles count what was collected. The tabs switch the panel between the 13 formats, Copy takes the whole file, and Download saves it under that format's name, such as _tokens.scss. The inventory below the panel lists the export group by group, with every color shown in HEX, RGB, HSL and CMYK and rated for contrast on white. With nothing to export, the page names the nine blocks that produce tokens and links to the editor.

The 13 formats

Every format is written from the same collected tokens and differs only in what it can express. The last column is what each file carries. The samples below are one brand with one of everything.

FormatFileWhat it carries
CSStokens.cssAll 9 groups
SCSS_tokens.scssAll 9 groups
Lesstokens.lessAll 9 groups
JS and TStokens.tsAll 9 groups
Tailwindtailwind.tokens.jsAll 9 groups
JSONtokens.jsonAll 9 groups
W3C Tokenstokens.w3c.jsonAll 9 groups
Style Dictionarytokens.sd.jsonColor, Typography, Spacing scale, Grid and layout, Motion and Themes
SwiftUIBrandColors.swiftColor
ComposeBrand.ktColor and Spacing scale
Flutterbrand.dartColor and Spacing scale
Android XMLcolors.xmlColor and Spacing scale
CSVcolors.csvColor

The five web files

CSS

Custom properties under :root. Each color brings an -rgb and an -hsl companion, so a rule can set an alpha. Each theme is a block keyed by a data attribute.

tokens.css
:root {
  --color-primary: #c2542c;
  --color-primary-rgb: 194, 84, 44;
  --color-primary-hsl: 16, 63%, 47%;
  --font-heading: "Inter";
  --text-md: 1rem;
  --leading-md: 1.5;
  --text-lg: 1.25rem;
  --leading-lg: 1.42;
  --tracking-lg: -0.01em;
  --spacing-8: 8px;
  --spacing-16: 16px;
  --breakpoint-md: 768px;
  --duration-fast: 150ms;
  --easing-standard: cubic-bezier(0.2, 0, 0, 1);
  --radius-md: 8px;
  --shadow-2: 0 2px 8px -1px rgba(0, 0, 0, 0.12);
}

[data-theme="dark"] {
  --theme-background: #111111;
  --theme-text: #f5f5f5;
}

SCSS

Sass variables, with the colors carrying an -rgb list. A theme role is a flat variable named after the theme and the role.

_tokens.scss
$color-primary: #c2542c;
$color-primary-rgb: (194, 84, 44);
$font-heading: "Inter";
$text-md: 1rem;
$leading-md: 1.5;
$text-lg: 1.25rem;
$leading-lg: 1.42;
$spacing-8: 8px;
$spacing-16: 16px;
$breakpoint-md: 768px;
$duration-fast: 150ms;
$easing-standard: cubic-bezier(0.2, 0, 0, 1);
$radius-md: 8px;
$shadow-2: 0 2px 8px -1px rgba(0, 0, 0, 0.12);
$theme-dark-background: #111111;
$theme-dark-text: #f5f5f5;

Less

One @ variable per token. No color companions, no tints and shades.

tokens.less
@color-primary: #c2542c;
@font-heading: "Inter";
@spacing-8: 8px;
@spacing-16: 16px;
@breakpoint-md: 768px;
@duration-fast: 150ms;
@easing-standard: cubic-bezier(0.2, 0, 0, 1);
@radius-md: 8px;
@shadow-2: 0 2px 8px -1px rgba(0, 0, 0, 0.12);
@theme-dark-background: #111111;
@theme-dark-text: #f5f5f5;
@text-md: 1rem;
@leading-md: 1.5;
@text-lg: 1.25rem;
@leading-lg: 1.42;

Tailwind

A config fragment under theme.extend, so every token arrives as a utility class. It fills colors, fontFamily, spacing, screens, transitionDuration, transitionTimingFunction, fontSize, borderRadius and boxShadow. Theme roles nest under colors, so a role is a class such as bg-theme-dark-background.

tailwind.tokens.js
/** Stylehub brand tokens */
module.exports = {
  theme: {
    extend: {
      colors: {
        "primary": "#c2542c",
        "theme-dark": {
          "background": "#111111",
          "text": "#f5f5f5",
        },
      },
      fontFamily: {
        "heading": ["Inter", "sans-serif"],
      },
      spacing: {
        "8": "8px",
        "16": "16px",
      },
      screens: {
        "md": "768px",
      },
      transitionDuration: {
        "fast": "150ms",
      },
      transitionTimingFunction: {
        "standard": "cubic-bezier(0.2, 0, 0, 1)",
      },
      fontSize: {
        "md": ["1rem", { lineHeight: "1.5" }],
        "lg": ["1.25rem", { lineHeight: "1.42" }],
      },
      borderRadius: {
        "md": "8px",
      },
      boxShadow: {
        "2": "0 2px 8px -1px rgba(0, 0, 0, 0.12)",
      },
    },
  },
};

JS and TS

A module of exported objects for JavaScript and TypeScript. Keys are camel cased and quoted. Spacing keys carry an s in front of the number. An empty group is still exported as an empty object.

tokens.ts
// Stylehub brand tokens

export const colors = {
  "primary": "#c2542c",
} as const;

export const fonts = {
  "heading": "Inter",
} as const;

export const spacing = {
  "s8": "8px",
  "s16": "16px",
} as const;

export const breakpoints = {
  "md": "768px",
} as const;

export const durations = {
  "fast": "150ms",
} as const;

export const easings = {
  "standard": "cubic-bezier(0.2, 0, 0, 1)",
} as const;

export const radii = {
  "md": "8px",
} as const;

export const shadows = {
  "2": "0 2px 8px -1px rgba(0, 0, 0, 0.12)",
} as const;

export const fontSizes = {
  "md": "1rem",
  "lg": "1.25rem",
} as const;

export const themes = {
  "darkBackground": "#111111",
  "darkText": "#f5f5f5",
} as const;

The three JSON documents

JSON

The product's own shape, and the only file with all four color notations at once: hex, rgb, hsl and cmyk, with the usage note. Tints and shades sit under colorScale.

tokens.json
{
  "color": {
    "primary": {
      "hex": "#c2542c",
      "rgb": "rgb(194, 84, 44)",
      "hsl": "hsl(16, 63%, 47%)",
      "cmyk": "cmyk(0, 57, 77, 24)",
      "usage": "Buttons and links"
    }
  },
  "colorScale": {},
  "font": {
    "heading": {
      "value": "Inter"
    }
  },
  "fontSize": {
    "md": {
      "value": "1rem",
      "lineHeight": 1.5,
      "tracking": "0em"
    },
    "lg": {
      "value": "1.25rem",
      "lineHeight": 1.42,
      "tracking": "-0.01em"
    }
  },
  "spacing": {
    "8": {
      "value": "8px"
    },
    "16": {
      "value": "16px"
    }
  },
  "breakpoint": {
    "md": {
      "value": "768px"
    }
  },
  "duration": {
    "fast": {
      "value": "150ms"
    }
  },
  "easing": {
    "standard": {
      "value": "cubic-bezier(0.2, 0, 0, 1)"
    }
  },
  "radius": {
    "md": {
      "value": "8px"
    }
  },
  "shadow": {
    "2": {
      "value": "0 2px 8px -1px rgba(0, 0, 0, 0.12)"
    }
  },
  "theme": {
    "dark": {
      "background": "#111111",
      "text": "#f5f5f5"
    }
  }
}

W3C Tokens

The W3C design tokens shape: every entry is a $value with a $type. Spacing and breakpoints share the dimension group, with the breakpoints prefixed. This is the document the API serves.

tokens.w3c.json
{
  "color": {
    "primary": {
      "$value": "#c2542c",
      "$type": "color"
    }
  },
  "dimension": {
    "8": {
      "$value": "8px",
      "$type": "dimension"
    },
    "16": {
      "$value": "16px",
      "$type": "dimension"
    },
    "breakpoint-md": {
      "$value": "768px",
      "$type": "dimension"
    }
  },
  "fontFamily": {
    "heading": {
      "$value": "Inter",
      "$type": "fontFamily"
    }
  },
  "fontSize": {
    "md": {
      "$value": "1rem",
      "$type": "dimension"
    },
    "lg": {
      "$value": "1.25rem",
      "$type": "dimension"
    }
  },
  "duration": {
    "fast": {
      "$value": "150ms",
      "$type": "duration"
    }
  },
  "cubicBezier": {
    "standard": {
      "$value": "cubic-bezier(0.2, 0, 0, 1)",
      "$type": "cubicBezier"
    }
  },
  "radius": {
    "md": {
      "$value": "8px",
      "$type": "dimension"
    }
  },
  "shadow": {
    "2": {
      "$value": "0 2px 8px -1px rgba(0, 0, 0, 0.12)",
      "$type": "shadow"
    }
  },
  "theme": {
    "dark": {
      "background": {
        "$value": "#111111",
        "$type": "color"
      },
      "text": {
        "$value": "#f5f5f5",
        "$type": "color"
      }
    }
  }
}

Style Dictionary

The source format a Style Dictionary build expects. Families sit under font.family rather than under size. It carries color, typography, spacing scale, grid and layout, motion and themes and nothing else: no type scale, no radii, no shadows.

tokens.sd.json
{
  "color": {
    "primary": {
      "value": "#c2542c"
    }
  },
  "font": {
    "family": {
      "heading": {
        "value": "Inter"
      }
    }
  },
  "size": {
    "spacing": {
      "8": {
        "value": "8px"
      },
      "16": {
        "value": "16px"
      }
    }
  },
  "breakpoint": {
    "md": {
      "value": "768px"
    }
  },
  "motion": {
    "duration": {
      "fast": {
        "value": "150ms"
      }
    },
    "easing": {
      "standard": {
        "value": "cubic-bezier(0.2, 0, 0, 1)"
      }
    }
  },
  "theme": {
    "dark": {
      "background": {
        "value": "#111111"
      },
      "text": {
        "value": "#f5f5f5"
      }
    }
  }
}

The four platform files

These are for an app rather than a page. Colors in all four, spacing in three, nothing else.

SwiftUI

Its own enum of colors, each as red, green and blue between 0 and 1, rather than an extension of Color. A name that starts with a digit is prefixed, and a Swift keyword has Color appended.

BrandColors.swift
import SwiftUI

enum BrandColors {
    static let primary = Color(red: 0.761, green: 0.329, blue: 0.173)
}

Compose

Kotlin objects for Android: BrandColors as Color(0xFF...) and BrandSpacing in dp. The package line at the top is a placeholder, so change it to yours.

Brand.kt
package com.yourcompany.brand

import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

object BrandColors {
    val Primary = Color(0xFFC2542C)
}

object BrandSpacing {
    val S8 = 8.dp
    val S16 = 16.dp
}

Flutter

The same two classes in Dart, with spacing as doubles.

brand.dart
import 'package:flutter/material.dart';

class BrandColors {
  static const primary = Color(0xFFC2542C);
}

class BrandSpacing {
  static const s8 = 8.0;
  static const s16 = 16.0;
}

Android XML

A resource file for a project that is not on Compose. Hyphens in a name become underscores, and spacing is written in dp. Colors and dimensions share one resources element.

colors.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- res/values/colors.xml -->
<resources>
    <color name="primary">#c2542c</color>
    <dimen name="spacing_8">8dp</dimen>
    <dimen name="spacing_16">16dp</dimen>
</resources>

The CSV

The palette as a spreadsheet: one header row, then a row per color with name, hex, rgb, hsl, cmyk and usage. Send it to a printer or to anyone who wants a plain list.

colors.csv
name,hex,rgb,hsl,cmyk,usage
"primary","#c2542c","rgb(194, 84, 44)","hsl(16, 63%, 47%)","cmyk(0, 57, 77, 24)","Buttons and links"

A cell is never a formula

A value that starts with an equals sign, a plus, a minus or an at sign gets a leading apostrophe, so a spreadsheet reads the cell as text rather than running it.

Every format at once

WhereDashboard, Export tokens, Download all formats (.zip)

One archive with all 13 files at the root, the Adobe swatch file beside them when the manual has at least one color, and under by-block one folder per group that produced something. Each of those folders holds that group alone as CSS, JSON, W3C Tokens and Tailwind. Take one folder and leave the rest.

brand-tokens.zip
tokens.css
_tokens.scss
tokens.less
tokens.ts
tailwind.tokens.js
tokens.json
tokens.w3c.json
tokens.sd.json
BrandColors.swift
Brand.kt
brand.dart
colors.xml
colors.csv
colors.ase
by-block/colors/tokens.css
by-block/colors/tokens.json
by-block/colors/tokens.w3c.json
by-block/colors/tailwind.tokens.js
by-block/typography/
by-block/type-scale/
by-block/spacing/
by-block/breakpoints/
by-block/motion/
by-block/radius/
by-block/elevation/
by-block/themes/

The file is named after the manual's slug, for example acme-tokens.zip. A manual with no slug is called brand. A manual with no tokens shows neither download button.

The Adobe swatch file

WhereDashboard, Export tokens, Download .ase (Adobe)

Download .ase (Adobe) writes an Adobe Swatch Exchange file for Illustrator, Photoshop and InDesign, named after the manual slug as acme-colors.ase. The button appears only when the manual has at least one readable color.

The first group is the palette, named after the manual, with each swatch under the color name as it was typed. A swatch left with no name is numbered by its position. Then comes one group per scale, named Primary tints and shades after the color it belongs to, with the eleven swatches inside. Only a Color block that shows its tints and shades produces one. Every swatch is RGB and global, so changing it repaints everything that uses it.

The same file is inside both archives: colors.ase in the all formats download and tokens/colors.ase in the brand kit. How the eleven steps are computed is in the Color article, under tints and shades.

The full brand kit

WhereDashboard, Export tokens, Download .zip

The kit is the other archive on the page: the tokens, a readable copy of the manual, and the asset library grouped by category. Send it to an agency that needs the brand rather than to a build that needs the values.

tokens/
tokens.css, tokens.json, colors.csv, tokens.w3c.json and tailwind.tokens.js, with colors.ase beside them when there are colors. For all 13, use the other archive.
brand-manual.md
The manual as readable text: colors with their hex and usage notes, typefaces, tone of voice traits, messaging and the written sections.
assets/
One folder per category. A file that is in several categories is filed under the first. With no files at all, a README.txt says so.

The kit needs the files permission too

It asks for View the brand manual and View assets. A role without the second sees a note in place of the Download .zip button. Folder rules apply as well: a folder your role may not see is not in your archive.

A kit holds just under four gigabytes of files, and a larger library is refused with a message naming both sizes. A file that could not be read is written empty and listed in _missing-files.txt at the end of the archive.

Reading the tokens instead of downloading them

The read only API serves the same W3C document as tokens.w3c.json, at one address per published manual. Use it so your build does not depend on a download. The exact address of every published manual is on the API page (opens in a new tab) of the dashboard.

Shell
curl -sS https://stylehub.cloud/api/v1/manuals/acme/tokens.json -o tokens.json
  • A public or unlisted manual needs no token. Any other visibility takes an API token of that brand. Tokens are made and revoked on the API page, which needs the permission Edit workspace settings and domains. See Authentication (opens in a new tab).
  • Keep the token secret. Do not put it in a public page: it reads every manual the brand has.
  • Calls a minute are limited by the plan of the brand being read, and counted per caller. See Rate limits (opens in a new tab), and Caching (opens in a new tab) for how to ask only when something changed.

The API serves the published manual

It serves published sections only, and the reviewed version when review before publishing is on. A color that is on the export page but not in the API answer is usually in a section you have not published yet.

Worked examples in PHP, Node, Python and C sharp are in the API examples (opens in a new tab), and the exact contract of every address is in the API documentation (opens in a new tab).

When a token you expected is missing

It is in another manual
The page reads the primary manual only. Read the other one over the API at its own address.
The hex cannot be read
A value that is not three or six hex digits is left out of every file. Retype it, or pick it with the color picker.
It is the second type scale
Only the first Type scale block is exported.
The format cannot hold it
Check the formats table, then take the CSS, JSON or W3C file instead.
The tints and shades are off
The eleven steps reach the files only for a Color block whose switch for them is on.

Where to go next

Still stuck? Write to hello@stylehub.cloud and say which page you were on.

Design tokens, in thirteen formats - Stylehub