Always Twisted

A Design Tokens Workflow (part 4)

Converting Tokens with Style Dictionary

  1. Getting Started With Style Dictionary
  2. Outputting to Different Formats with Style Dictionary
  3. Beyond JSON: Exploring File Formats for Design Tokens
  4. Converting Tokens with Style Dictionary – You are here
  5. Organising Outputs with Style Dictionary
  6. Layers, referencing tokens in Style Dictionary
On This Page:

Implementing Design Tokens can become the backbone of a consistent, scaleable Design System across your organisation and the platforms your products are on. However, different platforms have their own requirements for how names (keys) the values of Design Tokens are expressed and consumed. Whether it is dp for Android, points for iOS, rem for the web, ensuring your Design Tokens work seamlessly across platforms isn't a nice to have, it's essential. And it's possible using Style Dictionary.

Value conversion is the key to making this happen, it allows a single source of truth in your Design Tokens to adapt to the unique needs of each platform you work with. Whilst you might be defining your spacing tokens using pixels (16px), they may need to become rems (1rem) for your CSS, 16dp for Android, and 16pt for iOS. At the same time, tokens names like spacing.small may need to adapt to naming conventions using kebab-case (--spacing-small) for CSS, your team might want snake_case (spacing_small) for Android, and maybe the JavaScript developers are using camelCase (spacingSmall) throughout the rest of their project.

Without the ability to convert keys and values, a unified Design System could start to break down as it encounters nuances for each platform.

In this, the fourth article in my Design Tokens workflow series, we will explore how we can use Style Dictionary to handle both key and value conversion, ensuring your tokens are ready for the web, Android, iOS and beyond.

We'll look at practical examples of converting common token types like typography, spacing and colours and dive into how transforms and custom transform groups can adapt tokens for specific needs.

Adapting Token Names for Different Platforms

Before we take a look at how Style Dictionary can helps use convert the values of our Design Tokens for specific platforms, let's take a look at the possibilities of converting the name (key) of the Design Token.

Out-of-the-box Style Dictionary includes a range of name transform that can convert token keys into different formats. During the build process, these transforms map your source Design Tokens (spacing.small) into platform appropriate naming conventions.

Before we dive into how Style Dictionary can transform token keys and values to match the desired naming conventions and platform-specific requirements, let’s cover a quick housekeeping item. Moving forward, we’ll use the W3C Community Group’s specification for the .tokens file format to maintain consistency. We are also using version 4.x of Style Dictionary.

Here's an example of a Design Token

Code languagejson
{
"spacing": {
"small": {
"value": "8px",
"$type": "dimension",
"description": "Small spacing for compact padding or margins."
}
}
}

Built-in Name Transforms

Style Dictionary offers the following commonly used name transforms (there's a noticable change from v3 to v4 where the /cti has been removed – name/cti/kebab is now name/kebab):

  1. name/kebab - Converts names to kebab-case, ideal for CSS variables. Example: spacing.small--spacing-small
  2. name/camel - Converts names to camelCase, often used in JavaScript or JSON. Example: spacing.smallspacingSmall
  3. name/snake - Converts names to snake_case, commonly used for Android resource files. Example: spacing.smallspacing_small
  4. name/pascal - Converts names to PascalCase, often used for constants in Swift or Objective-C. Example: spacing.smallSpacingSmall

Transforming Keys for CSS

We need to register the converting of names and values as part of the specific transform group (platform (web, ios, Android, etc)).

Let’s say you’re generating CSS variables for your tokens and need the keys to follow kebab-case.

Code languagejavascript
import StyleDictionary from 'style-dictionary';
const myStyleDictionary = new StyleDictionary({
source: [`src/tokens/**/*.tokens`],
platforms: {
css: {
transformGroup: 'css',
buildPath: 'build/css/',
files: [
{
destination: 'all.css',
format: 'css/variables',
},
],
},
},
hooks: {
transformGroups: {
css: ['attribute/cti', 'name/kebab'],
},
},
});
// Build all platforms
myStyleDictionary.buildAllPlatforms();
console.log('Build completed!');
Code languagecss
:root {
--spacing-small: 8px;
}

If a team uses camelCase in their JavaScript projects, you can switch to the name/camel transform:

Code languagejavascript
import StyleDictionary from 'style-dictionary';
const myStyleDictionary = new StyleDictionary({
source: [`src/tokens/**/*.tokens`],
platforms: {
css: {
transformGroup: 'css',
buildPath: 'build/css/',
files: [
{
destination: 'all.css',
format: 'css/variables',
},
],
},
},
hooks: {
transformGroups: {
css: ['attribute/cti', 'name/camelCase'],
},
},
});
// Build all platforms
myStyleDictionary.buildAllPlatforms();
Code languagejavascript
export const tokens = {
spacingSmall: '8px'
};

When and Why to Transform Keys

Key conversion ensures your Design Tokens integrate seamlessly with platform-specific tools and conventions. By automating this step with Style Dictionary, you can:

  1. Eliminate manual renaming or inconsistent variable names.
  2. Create outputs that align with developer expectations and team standards.
  3. Simplify collaboration by generating tokens that “speak the language” of their target platform.

What is value conversion?

Design Tokens, at their core, is a shared language for design and development, but that shared language has to speak the specific dialect of each platform that will consume them.

Being able to convert the values of your Design Tokens ensures that the Design Tokens can remain universal, adapting their outputs to meet the need of those platforms and the environments they are implemented in.

Style Dictionary Transforms

Transforms in Style Dictionary work by applying rules to tokens during the build process, a transform might convert font sizes from px to rem for CSS or colour from hex (#ff0000) to UIColor formats for iOS. These built-in transforms can ensure that your tokens are translated into the correct units, formats, and structures. Much like add a custom parer as discussed in the third post in this series, Style Dictionary allows you to create custom transforms too.

Style Dictionary provides a set of built-in transforms that can take care of this value conversion for the most common use cases:

  1. Dimension Transforms These are used for spacing, sizing, and other dimensional tokens:

    • size/pxToRem: Converts pixel values (px) to rem values using a base font size (default: 16px), example: 16px1rem
    • size/remToDp: Converts pixel values to density-independent pixels (dp) for Android, example: 1rem16dp
    • size/remToPx: Converts rem values back to pixels (px) using a base font size, example: 1rem16px
  2. Colour Transforms These handle different colour formats for various platforms:

  1. Typography Transforms These handle font-related tokens:
  1. Timing and Animation Transforms Used for timing, easing, and animation properties:
  1. Miscellaneous Transforms These handle other token types:

Practical Use Cases for Value Conversion

Before we take a look at how we can use Style Dictionary to transform the values of the token to the desired values that can be consumed, a brief bit of housekeeping. Moving forward we are going to use the W3C community groups specification for their .tokens file format. I believe this should be the way forward to keep future posts consistent. So, let's get going and transform some values.

Converting px to rem for the Web

The configuration file for Style Dictionary adds the registration of a transform group, here we are converting px to rem and making sure the generated CSS uses kebab-case.

An example of a spacing design tokens set:

Code languagejson
{
"spacing": {
"xs": {
"$value": "4px",
"$type": "dimension",
"description": "Extra small spacing for minimal separations or fine-tuned padding."
},
"s": {
"$value": "8",
"$type": "dimension",
"description": "Small spacing for compact padding or margins."
},
"m": {
"$value": "16px",
"$type": "dimension",
"description": "Medium spacing for comfortable padding or margins."
},
"l": {
"$value": "24px",
"$type": "dimension",
"description": "Large spacing for larger separations in layouts."
},
"xl": {
"$value": "32px",
"$type": "dimension",
"description": "Extra large spacing for wide separations or white space."
}
}
}
Code languagejavascript
import StyleDictionary from 'style-dictionary';
const myStyleDictionary = new StyleDictionary({
source: [`src/tokens/**/*.tokens`],
platforms: {
css: {
transformGroup: 'css',
buildPath: 'build/css/',
files: [
{
destination: 'all.css',
format: 'css/variables',
},
],
},
},
hooks: {
transformGroups: {
css: ['attribute/cti', 'name/kebab', 'size/pxToRem'],
},
},
});
// Build all platforms
myStyleDictionary.buildAllPlatforms();
console.log('Build completed!');
Code languagecss
:root {
--spacing-xs: 0.25rem;
--spacing-s: 0.5rem;
--spacing-m: 1rem;
--spacing-l: 1.5rem;
--spacing-xl: 2rem;
}

To convert dimension values for other platforms Style Dictionary likes to have you use rem as the tokens value rather than px so you can make use of their built-in transforms.

Converting rem to dp for Android

An example of a spacing design tokens set:

Code languagejson
{
"spacing": {
"xs": {
"$value": ".25rem",
"$type": "dimension",
"description": "Extra small spacing for minimal separations or fine-tuned padding."
},
"s": {
"$value": ".5rem",
"$type": "dimension",
"description": "Small spacing for compact padding or margins."
},
"m": {
"$value": "1rem",
"$type": "dimension",
"description": "Medium spacing for comfortable padding or margins."
},
"l": {
"$value": "1.5rem",
"$type": "dimension",
"description": "Large spacing for larger separations in layouts."
},
"xl": {
"$value": "2rem",
"$type": "dimension",
"description": "Extra large spacing for wide separations or white space."
}
}
}

Style Dictionary includes the size/remToDp (converting rem to dp a) transform as part of its built-in Android transform group, which handles converting rem values into dp for Android. Although this is automatically included, here we are being explicit about its inclusion to show how you can modify or extend this configuration if needed.

Code languagejavascript
import StyleDictionary from 'style-dictionary';
const myStyleDictionary = new StyleDictionary({
source: [`src/tokens/**/*.tokens`],
platforms: {
android: {
transformGroup: 'android',
buildPath: 'build/android/',
files: [
{
destination: 'all.xml',
format: 'android/resources',
},
],
},
},
hooks: {
transformGroups: {
android: ['attribute/cti', 'name/snake', 'size/remToDp'],
},
},
});
// Build all platforms
myStyleDictionary.buildAllPlatforms();
console.log('Build completed!');

This gives us the relevant xml for Android.

<dimen name="spacing_xs">4.00dp</dimen> <dimen name="spacing_s">8.00dp</dimen> <dimen name="spacing_m">16.00dp</dimen> <dimen name="spacing_l">24.00dp</dimen> <dimen name="spacing_xl">32.00dp</dimen>

Converting color for Swift

Design Tokens often include colours that need to be adapted for different platforms. For example, in Swift development, colours are commonly represented using the UIColor class in Swift. Style Dictionary makes it easy to convert a colour token into this format using the color/UIColorSwift transform.

Code languagejson
{
"color": {
"primary": {
"$value": "#ff5733",
"$type": "color",
"description": "Primary brand colour used for buttons and links."
},
"secondary": {
"$value": "#33c1ff",
"$type": "color",
"description": "Secondary brand colour used for accents and highlights."
},
"tertiary": {
"$value": "#75ff33",
"$type": "color",
"description": "Tertiary brand colour used for backgrounds or subtle elements."
}
}
}

Style Dictionary includes the color/UIColorSwift transform in its built-in ios-swift group. While this transform group automatically converts hex colours into UIColor syntax, we’re explicitly defining the group to demonstrate how you could customise it if needed.

Code languagejavascript
import StyleDictionary from 'style-dictionary';
const myStyleDictionary = new StyleDictionary({
source: [`src/tokens/**/*.tokens`],
platforms: {
ios: {
transformGroup: 'ios',
buildPath: 'build/ios/',
files: [
{
destination: 'all.swift',
format: 'ios-swift/class.swift',
},
],
},
},
hooks: {
transformGroups: {
ios: ['attribute/cti', 'name/pascal', 'color/UIColorSwift'],
},
},
});
myStyleDictionary.buildAllPlatforms();
console.log('Build completed!');

This gives us a relevant .swift file:

import UIKit struct Colors { static let Primary = UIColor(red: 1.0, green: 0.34, blue: 0.2, alpha: 1.0) static let Secondary = UIColor(red: 0.2, green: 0.76, blue: 1.0, alpha: 1.0) static let Tertiary = UIColor(red: 0.46, green: 1.0, blue: 0.2, alpha: 1.0) }

Why Be Explicit?

While Style Dictionary provides sensible defaults with its transform groups, being explicit offers several advantages:

Clarity: Teams working on your Design System will immediately understand the transforms being applied without needing to look up defaults.

Flexibility: Explicitly defining the transforms makes it easier to customise them later, for example, if you add a custom transform.

Documentation: For educational purposes or shared configurations, showing the transforms provides a clear example of what’s happening under the hood.

Custom Transforms

Like the ability to create custom parsers for new file formats in Style Dictionary that we discussed in part 3 we also have the ability to create custom transforms for names (keys) and values of the Design Tokens.

Let's say we want to use the new color() possibilites of CSS and use color(display-p3) for the websites our Design System supports? We can do this with a custom transform.

First we would need to install a package that would help calculate the values to convert our hex colour code into display-p3. We can use chroma-js for this

Code languagebash
npm install chroma-js

Then we need to create a new transform in our Style Dictionary config file:

Code languagejavascript
// Ensure chroma-js is installed in your project
const chroma = require('chroma-js');
StyleDictionary.registerTransform({
// Unique name for the transform
name: 'color/hexToDisplayP3',
// This transform modifies the token's value
type: 'value',
// Applies only to tokens of type "color"
filter: (token) => token.$type === 'color',
// Convert hex to RGB and format as display-p3
transformer: (token) => {
const rgb = chroma(token.$value).rgb();
return `color(display-p3 ${rgb[0] / 255} ${rgb[1] / 255} ${rgb[2] / 255})`;
}
});

Now, we need to register a new transform group under the hooks.transformGroups property. This allows you to include your custom transform alongside other built-in transforms for a specific platform:

Code languagejavascript
module.exports = {
source: ['./tokens/*.tokens.json'],
platforms: {
css: {
transformGroup: 'web-p3',
buildPath: 'build/web/',
files: [{
destination: 'tokens.css',
format: 'css/variables'
}]
}
},
hooks: {
transformGroups: {
css: ['attribute/cti', 'name/kebab', 'color/hexToDisplayP3'],
},
};

This would give us a CSS file containing:

Code languagecss
:root {
--color-primary: color(display-p3 1 0.341176 0.2);
--color-secondary: color(display-p3 0.2 0.756862 1);
--color-tertiary: color(display-p3 0.458824 1.0 0.2);
}

Wrapping Up

Let's wrap the code we have looked at into a single configuration file, again the code for this post in the series is available on Github.

Code languagejavascript
import StyleDictionary from 'style-dictionary';
import chroma from 'chroma-js';
// Register additional transforms
StyleDictionary.registerTransform({
name: 'color/hexToDisplayP3',
type: 'value',
filter: (token) => token.$type === 'color',
transform: (token) => {
const rgb = chroma(token.$value).rgb(); // Possible issue here
return `color(display-p3 ${rgb[0] / 255} ${rgb[1] / 255} ${rgb[2] / 255})`;
}
});
// Configure Style Dictionary
const myStyleDictionary = new StyleDictionary({
source: [`src/tokens/**/*.tokens`],
platforms: {
css: {
transformGroup: 'css',
buildPath: 'build/css/',
files: [
{
destination: 'all.css',
format: 'css/variables',
},
],
},
android: {
transformGroup: 'android',
buildPath: 'build/android/',
files: [
{
destination: 'all.xml',
format: 'android/resources',
},
],
},
ios: {
transformGroup: 'ios-swift',
buildPath: 'build/ios/',
files: [
{
destination: 'all.swift',
format: 'ios-swift/class.swift',
},
],
},
},
hooks: {
transformGroups: {
css: ['attribute/cti', 'name/kebab', 'color/hexToDisplayP3'],
android: ['attribute/cti', 'name/snake', 'size/remToSp', 'color/hex8android'],
ios: ['attribute/cti', 'name/pascal', 'size/swift/remToCGFloat', 'color/UIColorSwift'],
},
},
});
// Build all platforms
myStyleDictionary.buildAllPlatforms();
console.log('Build completed!');

Here we have:

Taking these two .tokens file examples:

/tokens/color.tokens

Code languagejson
{
"color": {
"primary": {
"$value": "#ff5733",
"$type": "color",
"description": "Primary brand colour used for buttons and links."
},
"secondary": {
"$value": "#33c1ff",
"$type": "color",
"description": "Secondary brand colour used for accents and highlights."
},
"tertiary": {
"$value": "#75ff33",
"$type": "color",
"description": "Tertiary brand colour used for backgrounds or subtle elements."
}
}
}

/tokens/spacing.tokens

Code languagejson
{
"spacing": {
"xs": {
"$value": ".25rem",
"$type": "dimension",
"description": "Extra small spacing for minimal separations or fine-tuned padding."
},
"s": {
"$value": ".5rem",
"$type": "dimension",
"description": "Small spacing for compact padding or margins."
},
"m": {
"$value": "1rem",
"$type": "dimension",
"description": "Medium spacing for comfortable padding or margins."
},
"l": {
"$value": "1.5rem",
"$type": "dimension",
"description": "Large spacing for larger separations in layouts."
},
"xl": {
"$value": "2rem",
"$type": "dimension",
"description": "Extra large spacing for wide separations or white space."
}
},
}

We get these outputs:

/build/android/all.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- Do not edit directly, this file was auto-generated. --> <resources> <color name="color_primary">#ffff5733</color> <color name="color_secondary">#ff33c1ff</color> <color name="color_tertiary">#ff75ff33</color> <dimen name="spacing_xs">4.00dp</dimen> <dimen name="spacing_s">8.00dp</dimen> <dimen name="spacing_m">16.00dp</dimen> <dimen name="spacing_l">24.00dp</dimen> <dimen name="spacing_xl">32.00dp</dimen> </resources>

/build/ios/all.swift

// // all.swift // // Do not edit directly, this file was auto-generated. import UIKit public class { public static let colorPrimary = UIColor(red: 1.000, green: 0.341, blue: 0.200, alpha: 1) public static let colorSecondary = UIColor(red: 0.200, green: 0.757, blue: 1.000, alpha: 1) public static let colorTertiary = UIColor(red: 0.459, green: 1.000, blue: 0.200, alpha: 1) public static let spacingL = CGFloat(24.00) public static let spacingM = CGFloat(16.00) public static let spacingS = CGFloat(8.00) public static let spacingXl = CGFloat(32.00) public static let spacingXs = CGFloat(4.00) }

/build/css/all.css

Code languagecss
/**
* Do not edit directly, this file was auto-generated.
*/
:root {
--color-primary: color(display-p3 1 0.3411764705882353 0.2);
--color-secondary: color(display-p3 0.2 0.7568627450980392 1);
--color-tertiary: color(display-p3 0.4588235294117647 1 0.2);
--spacing-xs: .25rem;
--spacing-s: .5rem;
--spacing-m: 1rem;
--spacing-l: 1.5rem;
--spacing-xl: 2rem;
}

Adapting Design Token names (keys) and values is essential for creating a scalable and maintainable Design System. These practices ensure that your tokens align with the specific needs of each team and platform while maintaining consistency across your Design System. By providing platform-specific outputs, developers can seamlessly integrate tokens into their workflows, fostering better collaboration across teams.

Hopefully, this article has clarified how to adapt Design Tokens effectively for different platforms. By utilising transforms and transform groups in Style Dictionary, you can ensure your Design System remains consistent and scalable while meeting the unique requirements of every platform your team supports.

Unsure how to structure your design tokens for scalability and future growth?

I can help define a scalable structure for your tokens, ensuring they grow with your system.

get in touch!