Migration Guidelines
Version 4 of Style-Dictionary comes with a good amount of breaking changes compared to version 3.
In this document, we will outline those breaking changes, ordered from most impactful to least. Before and after examples are added to enable you to adjust your code to account for the changes.
ES Modules instead of CommonJS
There are different ways to write JavaScript, NodeJS came up with CommonJS format and later browsers brought ES Modules format which NodeJS also supports. ES Modules is nowadays considered the modern way to write JavaScript, NodeJS/Browser interoperability being only 1 of many reasons for that.
Therefore, in version 4, Style Dictionary has been entirely rewritten in ES Modules, in a way that is browser-compatible out of the box, allowing you to run it in many more places compared to before.
What this means for you is that if you are using Style Dictionary in a CommonJS project, you will have to either:
- convert your project to ESM by putting
"type": "module"
in yourpackage.json
and migrating your JS files to be ESM format. Note that you usually can still import CommonJS dependencies from ESM files, although this is not possible in browser environments. - use
.mjs
extension for your Style Dictionary consuming code, but keep the rest of your project as CommonJS. - dynamically import Style Dictionary into your CommonJS files
const StyleDictionary = (await import('style-dictionary')).default;
- use a bundler tool that allows ESM / CommonJS interoperability, meaning you can combine both syntaxes. If I’m not mistaken, bun (NodeJS alternative) supports that by default
The above options are ordered, the top being the option we recommend the most, but this ordering is highly subjective.
Instantiating Style Dictionary
Style Dictionary has become a class now in version 4 rather than just a regular JS object.
This means that you can create instances using the new
class instantiator keyword.
Due to ES Modules being asynchronous by nature, you will need to await
initialization before you can access properties such as the tokens
on the instance.
.extend()
method is still available on Style Dictionary instances if you want to create an instance (extend) from another instance.
See the extend docs.
Asynchronous API
There are a couple of reasons for making most of Style Dictionary’s methods asynchronous but the 2 most important reasons are:
- ES Modules are asynchronous by nature, so many of the internal processes such as importing a config or token file are now async, causing other methods to become async as well by extension.
- Hooks now support asynchronous methods, to make them easier to work with. Example: you may want to run Prettier on your output files in your custom format, Prettier recently became async which means your format function would need to be async as well.
The following StyleDictionary class methods are now async:
extend()
exportPlatform()
getPlatform()
buildAllPlatforms()
buildPlatform()
cleanAllPlatforms()
cleanPlatform()
All hooks now support async functions as well, this should not be a breaking change for users since sync is also still supported.
One exception is our fileHeader
format helper utility, this is now an async function to support async fileHeaders.
Hooks APIs
We’ve given a name to all of the things that you can register which will execute custom behavior during the Style Dictionary lifecycle: hooks
.
Available hooks are: parsers
, preprocessors
, transformGroups
, transforms
, formats
, filters
, fileHeaders
, actions
.
Parsers
Parsers, when registered, would always apply on a global level, without explicitly applying them in the config.
They are put inside the hooks.parsers
property now, as opposed to parsers
.
Lastly, the parse
function is now parser
, for consistency.
Changes:
Preprocessors
Preprocessors, when registered, would always apply on a global level, without explicitly applying them in the config.
They are put inside the hooks.preprocessors
property now, as opposed to preprocessors
.
Changes:
Transform Groups
Transform groups, when registered, are put inside the hooks.transformGroups
property now, as opposed to transformGroup
.
Note the change from singular to plural form here.
Changes:
Transforms
Transforms, when registered, are put inside the hooks.transforms
property now, as opposed to transform
.
Note the change from singular to plural form here.
The name of the filter function is now filter
instead of matcher
:
Lastly, the transformer
handler function has been renamed to transform
for consistency.
Changes:
Formats
Formats, when registered, are put inside the hooks.formats
property now, as opposed to format
.
Note the change from singular to plural form here.
The formatter
handler function has been renamed to format
for consistency.
Lastly, some importable type interfaces have been renamed as well.
Changes:
File headers
File headers, when registered, are put inside the hooks.fileHeaders
property now, as opposed to fileHeader
.
Note the change from singular to plural form here.
Filters
Filters, when registered, are put inside the hooks.filters
property now, as opposed to filter
.
Note the change from singular to plural form here.
In addition, when using registerFilter
method, the name of the filter function is now filter
instead of matcher
:
Actions
Actions, when registered, are put inside the hooks.actions
property now, as opposed to action
.
Note the change from singular to plural form here.
CTI reliance
CTI or Category / Type / Item used to be the default way of structuring design tokens in Style Dictionary.
Often, what type of token a token is, would be determined by looking at the “category” part of the token taxonomy.
Most of the Built-in transforms matcher
/filter
(filter being the new name for this) function would rely on the token’s attributes.category
property.
This in turn would rely on applying the attribute/cti
transform so that this attribute was set on a token.
In version 4, we have removed almost all hard-coupling/reliances on CTI structure and instead we will look for a token.type
property to determine what type of token a design token is.
This aligns more with the Design Tokens Community Group specification for standardizing design tokens JSON format.
Additionally, the following transforms have changed:
- Built-in name transforms are now reliant only on the token path, and are renamed from
name/cti/casing
to justname/casing
.name/ti/camel
andname/ti/constant
have been removed. For examplename/cti/kebab
transform is nowname/kebab
. - Transform
content/icon
has been renamed tohtml/icon
since it targets HTML entity strings, not just any icon content. font/objC/literal
,font/swift/literal
andfont/flutter/literal
have been removed in favor ofcontent/objC/literal
,content/swift/literal
andcontent/flutter/literal
, as they do he exact same transformations.
Package Entrypoints
We’ve adopted package entrypoints, which is also referred to as export maps.
What this means is, our package.json
contains the following:
This allows Style Dictionary consumers to only import from those 4 entrypoints specified:
Any other imports e.g. directly from a file path is disallowed and most modern package entrypoints supporting tools will not let you import from them. This sets a clear and explicit boundary between what is considered public API versus private API, and prevents accidentally running into breaking changes in the future.
Due to not allowing to import from file paths directly anymore, this is considered a breaking change.
Format Helpers
We moved the format helpers away from the StyleDictionary module/class into the utils entrypoint, for consistency in our API.
Formatting options
Using file formats, it is possible to pass options that influence how your output is created, and these options differ based on which format you are using.
In v3, the following options were put on the file properties level itself next to destination
and format
props, but have been moved into the options
property:
className
-> for formats:compose/object
flutter/class.dart
ios-swift/any.swift
ios/colors.h
ios/colors.m
ios/singleton.h
ios/singleton.m
ios/static.m
ios/strings.h
ios/strings.m
packageName
-> for formats:compose/object
type
-> for formats:ios/colors.h
ios/colors.m
ios/singleton.h
ios/singleton.m
ios/static.h
ios/static.m
mapName
-> for formats:scss/map-deep
scss/map-flat
name
-> for formats:javascript/object
javascript/umd
resourceType
-> for formats:android/resources
resourceMap
-> for formats:android/resources
fileHeader default timestamp
For all formats using the fileHeader
formatHelpers
utility (most of the built-ins do), it will no longer display a timestamp in the fileHeader output by default. This is now an opt-in by setting file.formatting.fileHeaderTimestamp
to true
. The reason for making this opt-in now is that using Style Dictionary in the context of a CI (continuous integration) pipeline is a common use-case, and when running on pull request event, output files always show a diff in git due to the timestamp changing, which often just means that the diff is bloated by redundancy.
To achieve the old behavior:
or:
Types
Style Dictionary is entirely strictly typed now, and there will be .d.ts
files published next to every file, this means that if you import from one of Style Dictionary’s entrypoints, you automatically get the types implicitly with it. This is a big win for people using TypeScript, as the majority of the codebase now has much better types, with much fewer any
s.
If you need to import some specific type interfaces separately, you can do so from the style-dictionary/types
entrypoint.
typescript/module-declarations
format is updated with current DesignToken type interface, and type interface changes are technically always breaking, which is why it’s mentioned here.
Reference utils
Our reference utilities are now available from style-dictionary/utils
entrypoint rather than attached to your StyleDictionary instance.
We’ve also updated the function signatures of the reference utilities to address this change and make them easier to reuse as well as more consistent in their APIs.
In addition, we’ve added a resolveReferences utility to make it easy to get the resolved value of a token.
OutputReferences function
We now allow specifying a function
for outputReferences
, conditionally outputting a ref or not per token.
We also published an outputReferencesFilter
utility function which will determine whether a token should be outputting refs based on whether those referenced tokens were filtered out or not.
If you are maintaining a custom format that allows outputReferences
option, you’ll need to take into account that it can be a function, and pass the correct options to it.
Logging
Logging has been redesigned a fair bit and is more configurable now, see Logging docs.
Instead of only being able to specify log: "error"
to change the default behavior of the logging to throw warnings as errors,
you can now also customize the verbosity of the logs and silence warnings and success logs.
Assets in CSS
We no longer wrap tokens of type asset
in double quotes.
Rather, we added a transform asset/url
that will wrap such tokens inside url("")
statements, this transform is applied to transformGroups scss
, css
and less
.
You may need to update your custom transforms if you were doing this transformation on your end, since it’s now being done by default in those transformGroups.
Removed Deprecated features
templates
andregisterTemplate
, useformats
andregisterFormat
insteadproperties
/allProperties
props on the StyleDictionary instance
format.format
old function signature of(dictionary, platform, file)
in favor of({ dictionary, platform, options, file })
.