Always Twisted

A Design Tokens Workflow (part 7)

Implementing Light and Dark Mode 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
  5. Organising Outputs with Style Dictionary
  6. Layers, referencing tokens in Style Dictionary
  7. Implementing Light and Dark Mode with Style Dictionary – You are here
  8. Implementing Light and Dark Mode with Style Dictionary (part 2)
On This Page:

In this article, we will explore how to implement light and dark modes in your design system using design tokens with Style Dictionary. We’ve previously covered formats, value conversion, extending the design tokens format, and design token layers. Today, we will build upon those concepts to effectively manage design tokens for both modes and generate the necessary CSS to ensure a seamless transition. |

A Quick Look at Dark and Light Mode

With the introduction of the ~prefers-color-scheme~ media feature, websites can now provide additional theming options for their visitors by implementing ‘dark mode’ and ‘light mode’. Light mode typically features a bright background with dark text, which is well-suited for use in well-lit environments. Conversely, dark mode employs a dark background with light text, creating a visually comfortable experience for users in low-light conditions. This mode not only reduces eye strain but also enhances readability, making it a popular choice for many applications. The ability to toggle between these modes has become increasingly important as users seek personalised experiences that cater to their preferences and environments. By leveraging the prefers-color-scheme media query, we can automatically adjust styles based on the user's system settings, ensuring a seamless transition between modes. This adaptability is crucial for modern design systems, as it enhances user satisfaction and accessibility.

The Role of Design Tokens

Design tokens provide a consistent and reusable framework for defining colours, spacing, and other design attributes, facilitating easy management and switching between modes without the need to rewrite styles or adjust individual components. For instance, semantic tokens can be created to specify background and text colours for both light and dark modes, allowing for a seamless transition based on user preferences. Moreover, design tokens facilitate scalability and maintainability within a design system. By using a single source of truth for colours and styles, teams can ensure that updates to the design are applied uniformly across all components. This consistency is particularly important when implementing light and dark modes, as it helps maintain visual harmony and accessibility across different modes. With design tokens, adjusting the appearance of an entire application becomes a straightforward process, enhancing user experience and satisfaction.

Ways to Author Design Tokens

In this article, we will explore three methods for creating CSS for light and dark modes using design tokens. Each approach offers unique advantages and can be tailored to fit the needs of your design system.

  1. Multiple Files for Light and Dark Modes (Two CSS Files): In this method, we will create separate token files for light and dark modes. This setup allows us to generate two distinct CSS files, one for each mode, ensuring clear separation and easy management of styles. Each file will reference its corresponding semantic tokens, making it straightforward to maintain and update styles.
  2. Multiple Files for Light and Dark Modes (One CSS File): Similar to the first method, this approach also involves creating separate token files for light and dark modes. However, in this case, we will generate a single CSS file that incorporates styles from both modes. This can be achieved using the prefers-color-scheme media feature to apply the appropriate styles based on user preferences.
  3. Single Token File Incorporating Light and Dark Modes (One CSS File): In this approach, we will author a single token file that contains both light and dark mode styles. This method allows for a more cohesive view of the design tokens, facilitating easier management and updates. We will then generate a single CSS file that includes all necessary styles, using a combination of CSS custom properties and media queries to handle the mode switching. By exploring these three methods, we can demonstrate the flexibility and power of design tokens in managing light and dark modes effectively. Now, let’s dive into the code examples for each approach.

Getting Started

First, we will look at the multiple JSON files we need to create to generate the appropriate CSS files.

Creating some Design Tokens

Base Tokens Base tokens are the fundamental building blocks of your design system. They represent the raw, literal values that define colours, spacing, typography, and more. Let's create a short set of base tokens that we can reference to create our light and dark modes. tokens/base/colors.tokens

Code languagejson
{
"color": {
"base": {
"white": {
"$value": "#FFFFFF",
"$type": "color"
},
"black": {
"$value": "#000000",
"$type": "color"
},
"gray": {
"100": {
"$value": "#F7F7F7",
"$type": "color"
},
"200": {
"$value": "#E1E1E1",
"$type": "color"
},
"300": {
"$value": "#CFCFCF",
"$type": "color"
},
"400": {
"$value": "#B3B3B3",
"$type": "color"
},
"500": {
"$value": "#A0A0A0",
"$type": "color"
},
"600": {
"$value": "#7D7D7D",
"$type": "color"
},
"700": {
"$value": "#5A5A5A",
"$type": "color"
},
"800": {
"$value": "#3D3D3D",
"$type": "color"
},
"900": {
"$value": "#1A1A1A",
"$type": "color"
}
},
"primary": {
"$value": "#007BFF",
"$type": "color"
},
"secondary": {
"$value": "#6C757D",
"$type": "color"
}
}
}
}

Semantic Tokens Now that we've got a simple set of base tokens, we can build on these with semantic tokens, which provide context and meaning. They map to the base tokens and define how these colours should be used in different modes. To effectively manage light and dark modes, we can create separate semantic token files—one for light mode and one for dark mode. tokens/semantic/colors.tokens

Code languagejson
{
"color": {
"background": {
"$value": "{color.base.white}",
"$type": "color",
"$description": "Background color for light mode."
},
"text": {
"$value": "{color.base.black}",
"$type": "color",
"$description": "Text color for light mode."
},
"card": {
"background": {
"$value": "{color.base.gray.200}",
"$type": "color",
"$description": "Background color for cards in light mode."
},
"border": {
"$value": "{color.base.gray.600}",
"$type": "color",
"$description": "Border color for cards in light mode."
}
}
},
"button": {
"background": {
"primary": {
"$value": "{color.base.primary}",
"$type": "color",
"$description": "Primary button background color in dark mode."
},
"secondary": {
"$value": "{color.base.secondary}",
"$type": "color",
"$description": "Secondary button background color in dark mode."
}
},
"text": {
"$value": "{color.base.white}",
"$type": "color",
"$description": "Text color for buttons in dark mode."
}
}
}

tokens/semantic/colors.dark.tokens

Code languagejson
{
"color": {
"background": {
"$value": "{color.base.gray.500}",
"$type": "color",
"$description": "Background color for dark mode."
},
"text": {
"$value": "{color.base.white}",
"$type": "color",
"$description": "Text color for dark mode."
},
"card": {
"background": {
"$value": "{color.base.gray.800}",
"$type": "color",
"$description": "Background color for cards in dark mode."
},
"border": {
"$value": "{color.base.gray.500}",
"$type": "color",
"$description": "Border color for cards in dark mode."
}
}
}
}

In this structure, we have separate semantic token files for light and dark modes:

Setting Up Style Dictionary

We need to create a ⁠build.js (or config, or whatever you like to call it) file to set up Style Dictionary to manage these design tokens for light and dark modes. The file will define how the tokens are processed and how the resulting CSS files are generated. Below is the complete code for the ⁠build.js file, followed by an explanation of its key components.

Code languagejs
import { globSync } from 'glob';
import StyleDictionary from 'style-dictionary';
const tokenFiles = globSync('src/tokens/**/*.tokens');
const HEADER_COMMENT = `/**
* Do not edit directly, this file was auto-generated.
*/\n\n`;
const myStyleDictionary = new StyleDictionary({
source: tokenFiles,
platforms: {
css_base: {
transformGroup: 'css',
buildPath: 'build/css/base/',
files: [
{
destination: 'colors.css',
format: 'css/variables',
filter: (token) => token.filePath.includes('base'),
},
],
},
css_semantic: {
transformGroup: 'css',
buildPath: 'build/css/semantic/',
files: [
{
destination: 'colors.css',
format: 'css/variables',
filter: (token) => token.filePath.includes('semantic'),
options: {
outputReferences: true,
},
},
{
destination: 'colors.dark.css',
format: 'css/variables-dark',
filter: (token) => token.filePath.includes('semantic') && token.filePath.includes('dark'),
options: {
outputReferences: true,
},
},
],
},
},
});
StyleDictionary.hooks.formats['css/variables-dark'] = function({ dictionary, options }) {
const { outputReferences } = options;
const darkTokens = dictionary.allTokens.filter(token =>
token.filePath.includes('semantic') && token.filePath.includes('dark')
);
const tokens = darkTokens.map((token) => {
const { name } = token;
const baseVariableName = token.original['$value'].replace(/^\{|\}$/g, ''); // Remove curly braces
const cssVariableName = `var(--${baseVariableName.replace(/\./g, '-').replace(/_/g, '-')})`;
const description = token.original.$description;
return ` --${name}: ${cssVariableName};${description ? ` /* ${description} */` : ''}`;
}).join('\n');
return `${HEADER_COMMENT}@media (prefers-color-scheme: dark) {\n :root {\n${tokens}\n }\n}`;
};
myStyleDictionary.buildAllPlatforms();
console.log('Build completed!');

Now let’s look at a couple of key parts of this JavaScript file:

Creating the Style Dictionary Instance

Code languagejs
const myStyleDictionary = new StyleDictionary({
source: tokenFiles,
platforms: {
css_base: {
transformGroup: 'css',
buildPath: 'build/css/base/',
files: [
{
destination: 'colors.css',
format: 'css/variables',
filter: (token) => token.filePath.includes('base'),
},
],
},
css_semantic: {
transformGroup: 'css',
buildPath: 'build/css/semantic/',
files: [
{
destination: 'colors.css',
format: 'css/variables',
filter: (token) => token.filePath.includes('semantic'),
options: {
outputReferences: true,
},
},
{
destination: 'colors.dark.css',
format: 'css/variables-dark',
filter: (token) => token.filePath.includes('semantic') && token.filePath.includes('dark'),
options: {
outputReferences: true,
},
},
],
},
},
});

In this section, we create a new instance of ⁠StyleDictionary and configure it to manage our token files. The configuration is divided into two main platforms:

  1. css_base: This platform generates a CSS file (⁠colors.css) for base tokens. The filter ensures that only tokens from the base category are included in this file.
  2. css_semantic: This platform generates CSS files for semantic tokens. It includes:
    • A ⁠colors.css file for general semantic tokens. This uses Style Dictionary’s filter so we can only generate tokens from the files that are in the folder ‘semantic’.
    • A ⁠colors.dark.css file specifically for dark mode tokens, using the ⁠variables-dark format. Again this uses Style Dictionary’s filter to look for tokens in the ‘semantic’ folder and also looks for ‘dark’ in the file name. We also use a custom format to compile the tokens to the CSS that we need.

Registering the ⁠variables-dark Format

Code languagejs
StyleDictionary.hooks.formats['css/variables-dark'] = function({ dictionary, options }) {
const { outputReferences } = options;
const darkTokens = dictionary.allTokens.filter(token =>
token.filePath.includes('semantic') && token.filePath.includes('dark')
);
const tokens = darkTokens.map((token) => {
const { name } = token;
const baseVariableName = token.original['$value'].replace(/^\{|\}$/g, ''); // Remove curly braces
const cssVariableName = `var(--${baseVariableName.replace(/\./g, '-').replace(/_/g, '-')})`;
const description = token.original.$description;
return ` --${name}: ${cssVariableName};${description ? ` /* ${description} */` : ''}`;
}).join('\n');
return `${HEADER_COMMENT}@media (prefers-color-scheme: dark) {\n :root {\n${tokens}\n }\n}`;
};

This custom format for ⁠variables-dark generates CSS custom properties specifically for dark mode. Here’s how it works:

Building All Platforms

Code languagejavascript
// Build all platforms
myStyleDictionary.buildAllPlatforms();
console.log('Build completed!');

Finally, this line triggers the build process for all configured platforms, generating the necessary CSS files based on the defined tokens and formats. The console log confirms that the build has completed successfully.

The Generated CSS

After running the build process, Style Dictionary generates several CSS files based on the defined tokens and formats. Here’s a breakdown of the outputs: base/colors.css This file contains the base colour tokens defined in your design system. It establishes CSS custom properties (variables) that represent fundamental colours used throughout your application. The output looks like this:

Code languagecss
/**
* Do not edit directly, this file was auto-generated.
*/
:root {
--color-base-white: #ffffff;
--color-base-black: #000000;
--color-base-gray-100: #f7f7f7;
--color-base-gray-200: #e1e1e1;
--color-base-gray-300: #cfcfcf;
--color-base-gray-400: #b3b3b3;
--color-base-gray-500: #a0a0a0;
--color-base-gray-600: #7d7d7d;
--color-base-gray-700: #5a5a5a;
--color-base-gray-800: #3d3d3d;
--color-base-gray-900: #1a1a1a;
--color-base-primary: #007bff;
--color-base-secondary: #6c757d;
}

These variables can be used throughout your CSS to maintain consistency in colour usage across different components (this will be the same CSS file for the other two examples also, so I won’t repeat it). semantic/colors.css This file contains the semantic colour tokens that define how the base colours should be applied in the context of your design system. It specifies colours for light and dark modes, ensuring that the appropriate styles are applied based on the chosen mode. The output includes:

Code languagecss
/**
* Do not edit directly, this file was auto-generated.
*/
:root {
--color-background: var(--color-base-gray-500); /* Background color for dark mode. */
--color-text: var(--color-base-white); /* Text color for dark mode. */
--color-card-background: var(--color-base-gray-800); /* Background color for cards in dark mode. */
--color-card-border: var(--color-base-gray-500); /* Border color for cards in dark mode. */
--button-background-primary: var(--color-base-primary); /* Primary button background color in dark mode. */
--button-background-secondary: var(--color-base-secondary); /* Secondary button background color in dark mode. */
--button-text: var(--color-base-white); /* Text color for buttons in dark mode. */
}

This file allows for easy management of colours specific to different components and contexts, ensuring that they adapt correctly to the selected mode. semantic/colors.dark.css This file is specifically for dark mode and uses the ⁠prefers-color-scheme media query to apply styles conditionally based on user preferences. The output looks like this:

Code languagecss
/**
* Do not edit directly, this file was auto-generated.
*/
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--color-base-gray-500); /* Background color for dark mode. */
--color-text: var(--color-base-white); /* Text color for dark mode. */
--color-card-background: var(--color-base-gray-800); /* Background color for cards in dark mode. */
--color-card-border: var(--color-base-gray-500); /* Border color for cards in dark mode. */
}
}

The styles defined here will only be applied when the user’s system is set to dark mode, ensuring that the application responds to user preferences and provides a comfortable viewing experience.

Multiple Files for Light and Dark Modes (One CSS File)

In this approach, we will create separate token files for light and dark modes, but instead of generating two distinct CSS files, we will consolidate the styles into a single CSS file. This method allows us to leverage the prefers-color-scheme media feature to apply the appropriate styles based on user preferences seamlessly.

Setting Up the Build Process

To implement this method, we will modify our build.js file to include a new format that combines the light and dark mode styles into one CSS file. Below is the complete code for the updated build.js file, followed by an explanation of its key components.

Code languagejavascript
import { globSync } from 'glob';
import StyleDictionary from 'style-dictionary';
const tokenFiles = globSync('src/tokens/**/*.tokens');
const HEADER_COMMENT = `/**
* Do not edit directly, this file was auto-generated.
*/\n\n`;
const myStyleDictionary = new StyleDictionary({
source: tokenFiles,
platforms: {
css_base: {
transformGroup: 'css',
buildPath: 'build/css/base/',
files: [
{
destination: 'colors.css',
format: 'css/variables',
filter: (token) => token.filePath.includes('base'),
},
],
},
css_semantic: {
transformGroup: 'css',
buildPath: 'build/css/semantic/',
files: [
{
destination: 'colors.css',
format: 'css/variables-combined',
options: {
outputReferences: true,
},
},
],
},
},
});
StyleDictionary.hooks.formats['css/variables-combined'] = function({ dictionary, options }) {
const { outputReferences } = options;
const semanticTokens = dictionary.allTokens.filter(token =>
token.filePath.includes('semantic')
);
const darkTokens = dictionary.allTokens.filter(token =>
token.filePath.includes('semantic') && token.filePath.includes('dark')
);
const semanticVariables = semanticTokens.map((token) => {
const { name, comment } = token;
const baseVariableName = token.original['$value'].replace(/^\{|\}$/g, '');
const cssVariableName = `var(--${baseVariableName.replace(/\./g, '-').replace(/_/g, '-')})`;
return ` --${name}: ${cssVariableName};${comment ? ` /* ${comment} */` : ''}`;
}).join('\n');
const darkVariables = darkTokens.map((token) => {
const { name, comment } = token;
const baseVariableName = token.original['$value'].replace(/^\{|\}$/g, '');
const cssVariableName = `var(--${baseVariableName.replace(/\./g, '-').replace(/_/g, '-')})`;
return ` --${name}: ${cssVariableName};${comment ? ` /* ${comment} */` : ''}`;
}).join('\n');
return `${HEADER_COMMENT}:root {\n${semanticVariables}\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n${darkVariables}\n }\n}`;
};
myStyleDictionary.buildAllPlatforms();
console.log('Build completed!');

Key Components of the Build Process

Creating the Style Dictionary Instance

Code languagejavascript
platforms: {
css_base: {
transformGroup: 'css',
buildPath: 'build/css/base/',
files: [
{
destination: 'colors.css',
format: 'css/variables',
filter: (token) => token.filePath.includes('base'),
},
],
},
css_semantic: {
transformGroup: 'css',
buildPath: 'build/css/semantic/',
files: [
{
destination: 'colors.css',
format: 'css/variables-combined',
options: {
outputReferences: true,
},
},
],
},
},

In this section, we configure a new instance of StyleDictionary to manage our token files. The configuration includes two main platforms:

  1. css_base: This platform generates a CSS file (colors.css) for base tokens, ensuring that only base category tokens are included.
  2. css_semantic: This platform generates a single CSS file for semantic tokens, utilising the new css/variables-combined format.

Registering the variables-combined Format

Code languagejavascript
StyleDictionary.hooks.formats['css/variables-combined'] = function({ dictionary, options }) {
const { outputReferences } = options;
const semanticTokens = dictionary.allTokens.filter(token =>
token.filePath.includes('semantic')
);
const darkTokens = dictionary.allTokens.filter(token =>
token.filePath.includes('semantic') && token.filePath.includes('dark')
);
const semanticVariables = semanticTokens.map((token) => {
const { name, comment } = token;
const baseVariableName = token.original['$value'].replace(/^\{|\}$/g, '');
const cssVariableName = `var(--${baseVariableName.replace(/\./g, '-').replace(/_/g, '-')})`;
return ` --${name}: ${cssVariableName};${comment ? ` /* ${comment} */` : ''}`;
}).join('\n');
const darkVariables = darkTokens.map((token) => {
const { name, comment } = token;
const baseVariableName = token.original['$value'].replace(/^\{|\}$/g, '');
const cssVariableName = `var(--${baseVariableName.replace(/\./g, '-').replace(/_/g, '-')})`;
return ` --${name}: ${cssVariableName};${comment ? ` /* ${comment} */` : ''}`;
}).join('\n');
return `${HEADER_COMMENT}:root {\n${semanticVariables}\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n${darkVariables}\n }\n}`;
};

The custom format for variables-combined generates CSS custom properties for both light and dark modes in a single file. Here’s how it works:

The Generated CSS

After running the build process, Style Dictionary generates a single CSS file that incorporates styles for both light and dark modes. Here’s a breakdown of the output:

semantic/colors.css

This file contains the combined CSS custom properties for light and dark modes, utilising the prefers-color-scheme media query. The output looks like this:

Code languagecss
/**
* Do not edit directly, this file was auto-generated.
*/
:root {
--color-background: var(--color-base-gray-500);
--color-text: var(--color-base-white);
--color-card-background: var(--color-base-gray-800);
--color-card-border: var(--color-base-gray-500);
--button-background-primary: var(--color-base-primary);
--button-background-secondary: var(--color-base-secondary);
--button-text: var(--color-base-white);
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--color-base-gray-500);
--color-text: var(--color-base-white);
--color-card-background: var(--color-base-gray-800);
--color-card-border: var(--color-base-gray-500);
}
}

Single Token File Incorporating Light and Dark Modes (One CSS File)

In this approach, we will author a single token file that contains both light and dark mode styles. This method allows for a more cohesive view of the design tokens, facilitating easier management and updates. We will then generate a single CSS file that includes all necessary styles, using a combination of CSS custom properties and media queries to handle the mode switching.

Creating the Design Tokens

Here’s an example of how the token file can be structured to incorporate both light and dark mode styles:

tokens/semantic/colors.tokens

Code languagejson
{
"color": {
"background": {
"$value": "{color.base.white}",
"$mods": {
"dark": "{color.base.black}"
},
"$type": "color",
"$description": "Background color for light mode."
},
"text": {
"$value": "{color.base.black}",
"$mods": {
"dark": "{color.base.white}"
},
"$type": "color",
"$description": "Text color for light mode."
},
"card": {
"background": {
"$value": "{color.base.gray.200}",
"$mods": {
"dark": "{color.base.gray.800}"
},
"$type": "color",
"$description": "Background color for cards in light mode."
},
"border": {
"$value": "{color.base.gray.600}",
"$mods": {
"dark": "{color.base.gray.400}"
},
"$type": "color",
"$description": "Border color for cards in light mode."
}
}
},
"button": {
"background": {
"primary": {
"$value": "{color.base.primary}",
"$type": "color",
"$description": "Primary button background color in dark mode."
},
"secondary": {
"$value": "{color.base.secondary}",
"$type": "color",
"$description": "Secondary button background color in dark mode."
}
},
"text": {
"$value": "{color.base.white}",
"$type": "color",
"$description": "Text color for buttons in dark mode."
}
}
}

You will notice in this .tokens file that we have introduce the $mode thing so that we can define additional values for things like dark mode or high contrast mode. This is (for now, possibly) not part of the specification, it comes from this (closed) github issue and I liked it 😊.

Setting Up the Build Process

To implement this method, we will modify our build.js file to include logic that handles both light and dark mode styles from a single token file. Below is the complete code for the updated build.js file, followed by an explanation of its key components.

Code languagejavascript
import { globSync } from 'glob';
import StyleDictionary from 'style-dictionary';
const tokenFiles = globSync('src/tokens/**/*.tokens');
const HEADER_COMMENT = `/**
* Do not edit directly, this file was auto-generated.
*/\n\n`;
const myStyleDictionary = new StyleDictionary({
source: tokenFiles,
platforms: {
css_base: {
transformGroup: 'css',
buildPath: 'build/css/base/',
files: [
{
destination: 'colors.css',
format: 'css/variables',
filter: (token) => token.filePath.includes('base'),
},
],
},
css_semantic: {
transformGroup: 'css',
buildPath: 'build/css/combined/',
files: [
{
destination: 'colors.css',
format: 'css/variables-combined',
options: {
outputReferences: true,
},
},
],
},
},
});
StyleDictionary.hooks.formats['css/variables-combined'] = function({ dictionary, options }) {
const semanticTokens = dictionary.allTokens.filter(token =>
token.filePath.includes('combined')
);
const lightVariables = semanticTokens.map((token) => {
const { name } = token;
const description = token.original.$description;
const baseVariableName = token.original['$value'].replace(/^\{|\}$/g, '');
const cssVariableName = `var(--${baseVariableName.replace(/\./g, '-').replace(/_/g, '-')})`;
return ` --${name}: ${cssVariableName};${description ? ` /* ${description} */` : ''}`;
}).join('\n');
const darkVariables = semanticTokens.map((token) => {
const { name } = token;
const description = token.original.$description;
const darkModeValue = token.original.$mods?.dark;
if (darkModeValue) {
const baseVariableName = darkModeValue.replace(/^\{|\}$/g, '');
const cssVariableName = `var(--${baseVariableName.replace(/\./g, '-').replace(/_/g, '-')})`;
return ` --${name}: ${cssVariableName};${description ? ` /* ${description} */` : ''}`;
}
return '';
}).filter(Boolean).join('\n');
return `${HEADER_COMMENT}:root {\n${lightVariables}\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n${darkVariables}\n }\n}`;
};
myStyleDictionary.buildAllPlatforms();
console.log('Build completed!');

Key Components of the Build Process

1. Creating the Style Dictionary Instance:

  1. Registering the variables-combined Format:

The Generated CSS

After running the build process, Style Dictionary (again) generates a single CSS file that incorporates styles for both light and dark modes. Here’s a breakdown of the output:

semantic/colors.css

Code languagecss
/**
* Do not edit directly, this file was auto-generated.
*/
:root {
--color-background: var(--color-base-white); /* Background color for light mode. */
--color-text: var(--color-base-black); /* Text color for light mode. */
--color-card-background: var(--color-base-gray-200); /* Background color for cards in light mode. */
--color-card-border: var(--color-base-gray-600); /* Border color for cards in light mode. */
--button-background-primary: var(--color-base-primary); /* Primary button background color in dark mode. */
--button-background-secondary: var(--color-base-secondary); /* Secondary button background color in dark mode. */
--button-text: var(--color-base-white); /* Text color for buttons in dark mode. */
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--color-base-black); /* Background color for dark mode. */
--color-text: var(--color-base-white); /* Text color for dark mode. */
--color-card-background: var(--color-base-gray-800); /* Background color for cards in dark mode. */
--color-card-border: var(--color-base-gray-400); /* Border color for cards in dark mode. */
}
}

Implementing light and dark modes using design tokens with Style Dictionary enhances user experience and adaptability. This article explored three effective methods for managing design tokens, providing flexibility in your design system.

  1. Multiple Files for Light and Dark Modes (Two CSS Files): This approach allows for clear separation of styles, generating distinct CSS files for each mode. View on GitHub
  2. Multiple Files for Light and Dark Modes (One CSS File): By consolidating styles into a single CSS file, this method leverages the ⁠prefers-color-scheme media feature to adapt based on user preferences. View on GitHub
  3. Single Token File Incorporating Light and Dark Modes (One CSS File): This cohesive approach simplifies management by using a single token file that includes both light and dark mode styles, facilitating easier updates and maintenance. View on GitHub

Need to integrate design tokens into your build tools or component libraries?

I’ll integrate tokens into your build process and component libraries for smooth workflows.

get in touch!