A Design Tokens Workflow (part 7)
Implementing Light and Dark Mode with Style Dictionary
- Getting Started With Style Dictionary
- Outputting to Different Formats with Style Dictionary
- Beyond JSON: Exploring File Formats for Design Tokens
- Converting Tokens with Style Dictionary
- Organising Outputs with Style Dictionary
- Layers, referencing tokens in Style Dictionary
- Implementing Light and Dark Mode with Style Dictionary – You are here
- 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.
- 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.
- 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.
- 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
{"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
{"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
{"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:
- Background tokens define the primary background colour for each mode.
- Text tokens specify the colour of the text based on the mode.
- Card tokens provide specific styles for card components, with background and border colours defined for light mode using {colour.gray.200} and {colour.gray.600}, and for dark mode using {colour.gray.800} and {colour.gray.500}.
- Button tokens specify the background colours for primary and secondary buttons in both modes, ensuring that the appropriate styles are applied based on the selected mode. This structure, with separate files for light and dark modes, helps maintain clarity and consistency across your design system. It makes it easier to manage colour transitions between modes and ensures a cohesive user experience. |
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.
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 bracesconst 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
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:
- 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.
- css_semantic: This platform generates CSS files for semantic tokens. It includes:
- A
colors.cs
s file for general semantic tokens. This uses Style Dictionary’sfilter
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’sfilter
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.
- A
Registering the variables-dark Format
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 bracesconst 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:
- The function filters the tokens to include only those that are semantic and marked for dark mode.
- It then maps these tokens to create CSS variable declarations, transforming the token names into valid CSS variable names.
- The output is wrapped in a media query that applies the styles only when the user prefers dark mode, ensuring that the CSS is conditionally applied based on user settings.
Building All Platforms
// Build all platformsmyStyleDictionary.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:
/*** 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:
/*** 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:
/*** 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.
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
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:
- css_base: This platform generates a CSS file (colors.css) for base tokens, ensuring that only base category tokens are included.
- css_semantic: This platform generates a single CSS file for semantic tokens, utilising the new css/variables-combined format.
Registering the variables-combined Format
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:
- Token Filtering: The function filters the tokens to include all semantic tokens and those specifically marked for dark mode.
- Variable Mapping: It maps the semantic tokens to create CSS variable declarations, transforming token names into valid CSS variable names.
- CSS Output: The output is structured to include a root declaration for light mode styles, followed by a media query for dark mode styles.
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:
/*** 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
{"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.
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:
- This instance is configured to manage our token files, including a platform for base tokens and a platform for semantic tokens that utilise the new css/variables-combined format.
- Registering the variables-combined Format:
- This custom format generates CSS custom properties for both light and dark modes from a single token file.
- It filters the tokens to extract both light mode values and dark mode overrides using the $mods property.
- The output is structured to include a root declaration for light mode styles, followed by a media query for dark mode styles.
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
/*** 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.
- 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
- 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
- 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