Skip to main content

Build

Building is the second step in the process. Run the build of your token translation tool directly. Although Theemo isn't running that command directly, it makes sense to include it as part of your scripts in your package.json as explained on getting started.

Style Dictionary

This is a manual to configure style dictionary for dark mode using the multi-file method.

In writer we already prepared for this by creating multiple files for tokens of different contexts. As a reminder, this is the generated file structure:

- tokens/
- intent/
- action/
- active.json
- base.json
- disabled.json
- hover.json
- alternatve/
- active.json
- base.json
- disabled.json
- hover.json
- brand.json
- hero.json
- layout.dark.json
- layout.light.json
- layout.transient.json
- sizing.json
- text.dark.json
- text.light.json
- text.transient.json
- typography.json

As described in the linked article above we will generate three config files, one for each context.

Base Configuration

Let's start with the config.base.js. The base config is sourcing tokens from anything non-dark and non-light token files. Also it takes in the transient tokens for reference purposes.

// config.base.js
const modes = ['light', 'dark'];

module.exports = {
include: ['tokens/**/*.transient.json'],
source: [
// this is saying find any files in the tokens folder
// that does not have .dark or .light, but ends in .json
`tokens/**/!(*.${modes.join(`|*.`)}).json`
],
platforms: {
web: {
transforms: [
'name/cti/kebab',
'time/seconds',
'content/icon',
'size/rem',
'color/css'
],
buildPath: 'build/',
files: [
{
format: 'css/variables',
destination: 'base.css',
options: {
outputReferences: true,
showFileHeader: false
},
filter(token) {
return !token.colorScheme;
}
}
]
}
},
modes
};

Thanks to the writer all metadata theemo knows about tokens have been written into style dictionary already and don't need extra transforms (such as attribute/cti) that would extract that information from token name.

Adding this to the scripts section in package.json:

{
"scripts:" {
"build:base": "style-dictionary build --config config.base.js"
}
}

Will enable executing the build:

❯ yarn build:base
yarn run v1.22.17
$ style-dictionary build --config config.base.js

web
✔︎ build/base.css
✨ Done in 0.59s.

Context Configurations

Next is the configuration for the contexts (light and dark). Taking advantage of the base configuration, including it and only configure deviating properties shall do the job. Here is the config for dark mode:

// config.dark.js
const base = require('./config.base');

const config = {
...base,
include: [`tokens/**/!(*.${base.modes.join(`|*.`)}).json`],
source: [`tokens/**/*.dark.json`]
};
config.platforms.web.files = [
{
format: 'css/variables',
destination: 'dark.css',
options: {
outputReferences: true,
showFileHeader: false
},
filter(token) {
return token.colorScheme === 'dark';
}
}
];

module.exports = config;

Adding the command to scripts in package.json:

{
"scripts:" {
"build:dark": "style-dictionary build --config config.dark.js"
}
}

and executing it:

❯ yarn build:dark
yarn run v1.22.17
$ style-dictionary build --config config.dark.js

web
✔︎ build/dark.css
✨ Done in 0.63s.

This will output a build/dark.css file. Repeat the same for light context to output a build/light.css file, too.

Full Build

Let's put everything together to run the full build. Pack every command into package.json to be able to trigger them individually as well as all together:

{
"scripts:" {
"build": "npm-run-all build:*",
"build:base": "style-dictionary build --config config.base.js",
"build:dark": "style-dictionary build --config config.dark.js",
"build:light": "style-dictionary build --config config.light.js"
}
}

Executing the build:

❯ yarn build
yarn run v1.22.17
$ npm-run-all build:*
$ style-dictionary build --config config.base.js

web
✔︎ build/base.css
$ style-dictionary build --config config.light.js

web
✔︎ build/light.css
$ style-dictionary build --config config.dark.js

web
✔︎ build/dark.css
✨ Done in 1.84s.

This will give us three files. But three files? One should be enough, right? That's exactly the job of generate to combine them together.