One of the biggest use-cases for CSS custom properties is theming. Let's consider that for a second. Perhaps you'd set some variables at the highest level you can:
html {
--column-bg: #ae0001;
--menu-bg-hover: #680001;
--active-item: #D3A625;
}
/* Anywhere else in the CSS, you use the variables */
.menu:hover, .menu:focus {
background: var(--menu-bg-hover);
}
Then if you want to change the theme, you have the opportunity to do that in one isolated place, which makes things tremendously easier to reason about. Here we'll allow a class change on that root element to change the variables. Anywhere those variables are used will instantly update.
html {
--column-bg: #ae0001;
--menu-bg-hover: #680001;
--active-item: #D3A625;
}
html.alternate-theme {
--column-bg: #680001;
--menu-bg-hover: #BE0002;
--active-item: #00FFBA;
}
/* Anywhere else in the CSS, you use the variables */
.menu:hover, .menu:focus {
background: var(--menu-bg-hover);
}
Here's a wonderful use of theming by Stephanie Liu. It's a recreation of the Slack interface's theming capabilities
Above, I showed how you might alter the values of CSS custom properties by changing a class, but JavaScript is capable of changing those variable values directly by selecting the element and passing a new value to it, like el.style.setProperty("--custom-property", "new-value");
, which is how Stephanie is doing it in this demo.
Like any good theming, a user's choice should persist. Stephanie handles this in this demo by plopping the selections into localStorage
, so check out her JavaScript to see how she's doing that.
Adjusting Theme Colors
Another consideration with CSS custom properties being in control of color themes is that sometimes you want a variation of a color, rather than having to pick a whole new second color. Like a button with the chosen color as a border, but a faded version for a background.
In a post by Ben Szabo, Theming with CSS variables in RGBA, he mentions you could use RGBa or HSLa to make those adjustments with opacity.
If the original color is in RGB or HSL to begin with, the opacity adjustment is easy:
:root {
--my-color-rgb: 255, 0, 0;
}
.btn {
border: 1px solid var(--my-color-rgb);
background: rgba(var(--my-color-rgb), 0.5);
}
I did a demo once where I laid a transparent black gradient over top the color to darken it, so it's not just lightening you can do but darkening as well:
Ana Tudor showed us we can make drastic changes to styling with the flip of a CSS custom property toggle.
She called it DRY switching.
The trick is rooted in math and the power of 0
and 1
. With calc()
, we can multiple values by zero and one and either get nothing or the original value. We can use that base trick for colors, positions, transforms... all kinds of stuff to get hugely different results when that flip is switched.
Interesting SVG hovers from Marius Niveri
I like this concept in which normal colored SVG has it's color temporarily removed with CSS, moved to a custom property, and then re-applied on hover. Like progressive enhancement coloring!
Keith Clark combined them with conic gradients to make pie charts
Pass the percentage in right from the markup:
<div class="pie pie--value" style="--percent:65;"></div>
Then use that value in a conic-gradient to draw the pie chart.
.pie {
background-image: conic-gradient(
rgba(0,0,0,0) calc(3.6deg var(--percent)),
rgba(0,0,0,1) calc(3.6deg var(--percent))
);
background-blend-mode: overlay;
background-position: 50% 50%;
background-size: 150%; /* oversize bg image to prevent "underdraw" */
width: 3.75em;
height: 3.75em;
border-radius: 50%;
}
Keith's demo is fancied up:
He's even got a bonus trick in there of using CSS counters to display the percentage value.
Sérgio Gomes aspect ratio trick with custom properties
What if you could tell an HTML element directly what aspect ratio you wanted it to be? That's documented here. You pass in a ratio directly from the a custom property on the element:
<div style="--aspect-ratio:16/9;">
</div>
Then use that value to apply a perfectly sized psuedo element inside that pushes it to that size.
[style="--aspect-ratio"] > :first-child {
width: 100%;
}
[style="--aspect-ratio"] > img { height: auto;
}
@supports (--custom:property) {
[style="--aspect-ratio"] {
position: relative;
}
[style="--aspect-ratio"]::before {
content: "";
display: block;
padding-bottom: calc(100% / (var(--aspect-ratio)));
}
[style*="--aspect-ratio"] > :first-child {
position: absolute;
top: 0;
left: 0;
height: 100%;
}
}
Clever stuff.
Top comments (5)
We're actually in the process of rolling out CSS variables on this site for the purpose of theming
[WIP] Theme-able CSS Variables #1377
I thought we ought to have a place where we keep a note of the current CSS Variables and also propose different variables that should be included.
Currently included:
--theme-background
#f9f9fa
--theme-top-bar-background
#fdf9f3
Pull request pending:
--theme-top-bar-color
#0a0a0a
--theme-top-bar-search-background
#e8e7e7
--theme-top-bar-search-color
#0a0a0a
Proposed:
--theme-color
#0a0a0a
--theme-container-background
#ffffff
--theme-container-color
#0a0a0a
I think the best thing to do is leave a comment below of further proposals and I will update the main post.
Also feel free to do pull requests to help roll these out.
If you'd like to contribute that would be greatly appreciated 🙂
Nice, I'm sure a lot of us would love a dark theme. I've been using the extension DarkReader at night, but it does not play nicely with dev.to so I've been waiting for a built-in option.
Custom properties look fairly straightforward to get started with. Your first example looks easy to implement for anyone familiar with CSS.
I'm glad to see that CSS is closing the gap with CSS preprocessors a little bit. Learning and configuring tools to transpile code can get a bit overwhelming at times.
very cool!
Good things and useful to know.....