CSS Variables vs. SASS Variables
Previously, I wrote a post about my thoughts on CSS variables. Initially, I didn’t have high expectations for this feature—being used to SASS variables, CSS variables felt like a clumsy system in comparison. But if you feel the same way, you might want to check out this article: Why should you care Css variables.
CSS variables have been around for a while now, and browser support isn’t too bad. You might have thought that almost everything CSS variables can do can already be done in SASS, and when combined with SASS functions and data types (lists, maps), variables become even more flexible and powerful.
However, what I want to share here is where CSS variables truly shine.
This article assumes that you already have some basic understanding of CSS variables.
As a side note, this post does not provide an exhaustive guide on how to use CSS variables. Any suggestions or feedback are welcome, and I’ll find time later to write a more comprehensive post.
Browser Support
| Browser | Version |
|---|---|
| Chrome | 49 |
| Firefox | 42 |
| Safari | 9.1 |
Custom Property
Only when you make it behave like a property does it become more powerful than traditional preprocessors.
The first thing to clarify is that in the CSS specification, variables are actually called custom properties. What does that mean? Variables vs. properties—it gets confusing!
Don’t worry. It simply means that when using CSS variables, we should think of them as regular CSS properties rather than just standard variables. Below, I’ll introduce some ways to use CSS variables.
Why You Should Use CSS Variables
- You don’t need a preprocessor to use these properties.
- You can establish hierarchies using
initialandinherit. - Browsers will re-render when needed.
- You can access and manipulate them with JavaScript (more on this later).
Property Characteristics
Since it is a CSS property, it naturally shares several characteristics:
- Values like
initialandinheritare available. - They are accessed via
var(--variable-name). - They can be used inside
inline-styles. - They can be read and set using JavaScript.
Among these, the biggest highlights are their usability in inline styles and the ability to read and set them with JS. This is the biggest difference compared to preprocessors like SASS and LESS, and it opens up new possibilities for CSS. Let’s look at some examples!
:root {
--mainColor: #abc;
--subColor: #ccc;
}
h1 {
color: var(--mainColor);
}
Here, the custom properties declared in :root will be inherited by all elements. That is the nature of a custom property.
inline-style
Because it is a property, you can naturally write it like this:
<h1 style="color: var(--mainColor);">
Hello, world
</h1>
This might not seem like a big deal at first glance, since the same outcome can be achieved with classes and SCSS variables, but it brings brand-new possibilities when writing inline styles.
React Inline Styles
For example, in React, we might need to write some inline styles. However, managing colors outside of SCSS often feels awkward. You either hardcode the color hex codes and roll up your sleeves to change them one by one later, maintain a separate color variable map in JS, or assign a class name for every single color variable (perhaps the best conventional approach).
Now, with CSS variables, it becomes very convenient:
const styles = {
heading: {
fontSize: "14px",
color: "var(--mainColor)",
},
warn: {
color: "var(--warnColor)",
},
}
const heading = ({ title }) => <h1 style={styles.heading}>{title}</h1>
This article only focuses on introducing CSS variables; in fact, there are other great ways to manage styles in React.
Grid
When configuring gutters, we can now achieve this using CSS variables. In other words, we can set variable values using @media queries—something traditional preprocessors cannot do.
JavaScript Control
Through JavaScript, we can easily read or set CSS variable values, achieving a separation between view and JS. For instance, when we have some values calculated in JS that need to be passed to CSS, traditionally, we might compute the value in JS and then apply it via an inline style.
However, this can lead to a few issues:
- The value might not be directly usable, or CSS might want to perform further operations on it. This requires modifying the JavaScript code, which is cumbersome.
- Because the value is applied via inline styles, CSS has very little control over it.
With CSS variables, reading and writing values is a breeze.
Reading a Value
HTMLElement.style.getPropertyValue("--mainColor")
Setting a Value
HTMLElement.style.setProperty("--mainColor", "#abc")
Therefore, we can easily attach event listeners to inject values of interest directly into our variables:
const element = document.addEventListener("mousedown", e => {
// some HTMLElement;
element.style.setProperty("--pageX", e.pageX)
element.style.setProperty("--pageY", e.pageY)
})
Feature Detection
If you want to check whether the current browser supports CSS variables, you can use the CSS.supports API:
CSS.supports('(--css: variables)') CSS.supports('not (--css: variables)')
Future Challenges
As there are more and more ways to manage variables—including setting them through JavaScript—we should be more disciplined when controlling them:
- Centralize variable declarations: Keep global variables organized in a single file or directory for easier maintenance later. The same applies when declaring CSS variables.
- If a variable is meant to be updated via JS, consider prefixing it as a marker, such as
--js-pageX, etc.
Conclusion
This article introduced the usage and scenarios for CSS variables. Previously, I also thought CSS variables were awkward—not only looking ugly in syntax, but also lacking built-in manipulation functions.
However, once you understand the concept that CSS variables are actually custom properties, you will discover possibilities that CSS previously could never achieve. There is no need to choose between SASS and CSS variables; we can even use both in tandem to create powerful effects that were once out of reach.
Related Posts
- Recreating My Room with Three.js Using React Three Fiber, I brought my real room into the browser—turning physical objects into an interactive table of contents, and using spatial memory to tell the story of my life and work over the past few years.
- Things to Keep in Mind When Using Images in Frontend Development Expanding on Jake Archibald's article, this post organizes how modern responsive images should be written: why width/height are still necessary, when to use CSS aspect-ratio, how to choose between AVIF and WebP, and using picture/source/srcset for art direction on mobile devices.
- CSS field-sizing — Auto-resize Form Elements with a Single Line of CSS Previously, auto-resizing a textarea required listening to scrollHeight in JavaScript. With CSS field-sizing: content, a single line replaces it all, supporting textarea, input, and select. This article covers the pain points of older approaches and how to use field-sizing.
- Make Your Link Underlines Look Better: text-underline-offset By default, underlines sit very close to the text. Some designers dislike this look, and personally, I don't think it looks great either.