Photo by Pankaj Patel on Unsplash

You don’t know CSS: Variables + Theming

Bryce Snyder
8 min readNov 19, 2023

--

TL; DR:

Theming with CSS variables involves using custom properties to store and reuse design values. You can easily change the look of a website by adjusting variables, providing benefits like consistency, real-time updates, and ease of maintenance. File-based theme swapping organizes themes into separate files, linked and switched dynamically using JavaScript. CSS variables can be imported locally, through CDNs, NPM, cloud storage, preprocessors, or dynamically via JavaScript. Best practices include consistent variable names, avoiding direct overrides, including fallback values, using specificity, and avoiding inline styles. Key benefits include consistency across applications, real-time updates, selective re-rendering, rapid prototyping, ease of maintenance, customization for branding, accessibility improvements, user personalization, cross-platform consistency, scalability, and design iteration facilitation. CSS variables can also be used for responsive typography and calculations for responsive margins and paddings. Overall, CSS variable theming is a powerful and flexible way to manage the visual aspects of a website.

Overview

Working alongside CSS variables is a powerful approach to managing the visual appearance of a website or application. It involves custom properties, commonly referred to as CSS variables, to store and reuse values such as colors, fonts, and other design-related properties.

What are CSS variables?

CSS variables are like containers that hold values you want to reuse throughout your stylesheet. They start with (double dash) and are often called custom properties.

:root {
--main-color: #3498db;
}

Here --main-color is a CSS variable holding the color #3498db.

What is theming?

Theming is the idea of changing the look and feel of your website or app easily by adjusting a few key values, such as colors or fonts.

How to use CSS variables for theming

Let’s say you want to create a theme where the main color can be easily changed. You use the variable where you would usually put the color value.

body {
background-color: var(--main-color);
}

Now, the background color of the body will be whatever color you set for --main-color.

Changing the theme

To switch themes, you only need to change the value of your CSS variable in one place (usually at the top of your stylesheet).

:root {
--main-color: #ff5733; /* new color for a different theme */
}

Now, your entire site will reflect the new color without having to change every instance manually.

File-based theme swapping.

Using different theme files with root-scoped CSS variables is a great way to organize and manage multiple themes. Here are additional points to showcase how theme swapping can be achieved with this approach:

Creating separate theme files

Instead of having all your styles in a single CSS file, create separate files for each theme. Each file must define its own set of CSS variables within the :root selector.

/* theme-light.css */
:root {
--main-color: #3498db;
/* other variables for light theme */
}
/* theme-dark.css */
:root {
--main-color: #1f2739;
/* other variables for dark theme */
}

Linking theme files

In your HTML file, link to the specific theme file you want to use. This might be based on user preference, time of day, or any other criteria.

<link rel="stylesheet" href="theme-light.css" id="theme-link">

JavaScript for theme switching:

Use JavaScript to dynamically switch between theme files. When the user selects a different theme, update the href attribute of the theme link to point to the corresponding theme file.

function setTheme(themeName) {
document.getElementById('theme-link').setAttribute('href', 'theme-' +
themeName + '.css');
}

User interaction for theme switching:

Create UI elements (buttons, toggles, etc.) that allow users to easily switch between themes. Attach event listeners to these elements to trigger the theme-switching function.

<button onclick="setTheme('light')">Light Theme</button>
<button onclick="setTheme('dark')">Dark Theme</button>

Organizing themes into individual files and using root-level CSS variables will enable you to have a modular and easily maintainable system for switching out themes in your web app.

How to import

CSS variables can be imported into your project using various methods and sources. Here are some common ways to import CSS variables, with potential sources you might use:

Local stylesheets

Create a local CSS file in your project and define the CSS variables directly within that file. This is the most straightforward and traditional method.

/* variables.css */
:root {
--main-color: #3498db;
--secondary-color: #ff5733;
/* other variables */
}

Import this file into your HTML:

<link rel="stylesheet" href="path/to/variables.css">

CDN (Content Delivery Network):

Use a CDN to host your CSS variables file. This allows you to easily share and include ‌variables in multiple projects.

<link rel="stylesheet" href="https://cdn.example.com/path/to/variables.css">

NPM (Node Package Manager):

If you are using a build tool like Webpack or Parcel, you can import CSS variables from an npm package. There are npm packages specifically designed to provide sets of CSS variables.

npm install my-css-variables-package
// In your JavaScript or SCSS file
import 'my-css-variables-package/style.css';

Cloud Storage:

Store your CSS variables file in cloud storage services like Google Cloud Storage, AWS S3, or Azure Blob Storage. This allows for easy versioning and sharing across projects.

<link rel="stylesheet" href="https://storage.example.com/path/to/variables.css">

CSS Preprocessors

If you are using a CSS preprocessor like SASS or LESS, you can import variables from external files and use them in your stylesheets.

// In your main.scss file
@import 'path/to/variables';

Dynamic loading via JavaScript

Load CSS variables dynamically using JavaScript. This is useful for scenarios where the variables need to be fetched or generated at runtime.

const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'path/to/variables.css';
document.head.appendChild(link);

Choose the method that best fits your project’s requirements and workflow. Local stylesheets are suitable for smaller projects, while CDNs, npm packages, and cloud storage options offer scalability and ease of sharing across different projects. The choice depends on factors such as project size, collaboration needs, and deployment considerations.

Best Practices

While root-scoped CSS variables provide a convenient way to manage theming, there are some practices you must avoid to ensure the smooth functioning of your theming system.

Consistency is key

Ensure that the variable names remain consistent across different theme files. This way, you can easily switch between themes without worrying about renaming variables in various style rules. Inconsistencies can lead to unexpected behavior when switching themes, making it challenging to maintain and debug.

/* theme-light.css */
:root {
--main-color: #3498db;
--font-color: #1f2739;
/* other variables for light theme */
}
/* theme-dark.css */
:root {
--main-color: #1f2739;
--font-color: #1f2739;
/* other variables for dark theme */
}

Avoid directly overriding root variables.

After you set a variable in the :root selector, it’s intended to be a global variable accessible throughout your stylesheets. However, directly overriding these variables can lead to unintended consequences and break the theming capabilities.

/* This is not recommended */
:root {
--main-color: #3498db;
}

body {
background-color: var(--main-color);
}

/* Avoid doing this later in the same stylesheet */
:root {
--main-color: #ff5733; /* This can cause unexpected behavior */
}

Include fallback values.

Include fallback values if a variable is not supported by a specific browser or environment. This guarantees a graceful degradation of styles.

:root {
--primary-color: #3498db;
}

.element {
background-color: var(--primary-color, #0077cc); /* Fallback value */
}

Use specificity instead of direct reassignment.

If you need to customize a variable in a specific context, use more specific selectors instead of redefining it globally. This makes sure that your changes are localized to the intended scope.

Preferred approach:

/* Preferred: use a more specific selector */
body {
--main-color: #3498db;
background-color: var(--main-color);
}

header {
--main-color: #ff5733; /* specific to the header */
background-color: var(--main-color);
}

.element {
--main-color: #c01818; /* specific to anything with element class */
}

Be cautious with inline styles.

Avoid setting root-scoped variables inline within HTML elements. While this might seem convenient, it can make your theming less maintainable and harder to manage.

What not to do:

<! - This is not recommended-->
<div style="--main-color: #3498db; background-color: var(--main-color);">
Content goes here.
</div>

By following these best practices and avoiding direct reassignments or inconsistent usage of root-scoped variables, you can maintain the flexibility and simplicity of your theming system.

Key Benefits

Leveraging theming capabilities, especially through a design system SDK (Software Development Kit), offers several benefits for creating consistent and customizable user interfaces:

Consistency across applications

Using a design system SDK with theming capabilities provides a consistent look and feel across different applications or components. This is crucial for building a cohesive brand identity.

Real-time updates:

When a CSS variable is modified, the changes are immediately reflected in the styles linked with that variable. This real-time update allows for dynamic theming, user-controlled preferences, or adjustments based on external factors without disrupting the user experience.

Re-rendering

Unlike some JavaScript-based solutions that might trigger a full re-render of the application when styles change, CSS variables only affect the elements using those variables. This selective updating prevents unnecessary re-renders of unaffected parts of the DOM, contributing to better performance.

Rapid prototyping and development

Theming allows developers to quickly prototype and develop different visual styles without rewriting or duplicating large parts of the code. Speeding up the development process and facilitating agile iteration.

Ease of maintenance

With theming, the styling logic is centralized through CSS variables. This makes it easier to maintain and update styles because changes can be made in one place (the theme file) and reflected throughout the application.

Customization for branding

Design system SDKs with theming capabilities empower developers to easily customize the appearance of an application to align with specific branding requirements. This is particularly useful in scenarios where applications need to adhere to various brand guidelines.

Accessibility improvements

Theming can be used to enhance accessibility by allowing users to select color schemes that suit their preferences or accommodate specific visual needs. This inclusivity is crucial for providing a positive user experience.

User preference and personalization

Theming supports user preference and personalization by enabling users to select from different themes. This might include light and dark modes, various color schemes, or even different font styles, enhancing the user experience based on individual preferences.

Cross-platform consistency

When building applications that span multiple platforms (web, mobile, desktop), theming through a design system SDK helps maintain a consistent visual language, making sure that users have a unified experience regardless of the platform they’re using.

Scalability and reusability

Design system SDKs with theming capabilities promote scalability and reusability of code. Components and styles can be reused across different projects, and the theming system allows for easy adaptation to different contexts.

Facilitates design iterations.

Designers can iterate on the visual aspects of an application more efficiently when they know that changes made to the design system are seamlessly reflected across the entire application, promoting collaboration between design and development teams.

Futureproofing

Theming allows applications to adapt to changing design trends or branding guidelines without requiring extensive code refactoring. This future-proofs the application by making it more adaptable to evolving design requirements.

Other Examples

CSS Variables aren’t only limited for use with theming. Here are some other ways you can leverage CSS Variables in your project:

Responsive Typography:

Use CSS variables to control font sizes and line heights responsively. Adjusting these variables based on viewport size can enhance readability and the overall user experience.

:root {
--base-font-size: 16px;
}

p {
font-size: var(--base-font-size);
line-height: calc(var(--base-font-size) * 1.5);
}

Calculation for Responsive Margins and Padding

Use CSS variables with calc() to create responsive margins and paddings based on a fixed or relative base value.

:root {
--base-spacing: 8px;
}

.box {
margin: var(--base-spacing);
padding: calc(var(--base-spacing) * 2);
}

Summary

CSS variable theming simplifies the process of changing the appearance of your website by using variables to store key values. By updating these variables, you can easily switch between themes without having to modify every individual style rule. It’s a powerful and flexible way to manage the visual aspects of your site.

--

--

Bryce Snyder

UX Engineering Manager @ McAfee Security with a passion for UI development & Design Systems