If you want your code to be more readily reusable between projects, we happily invite you to discover an improved way to use custom CSS properties.
2minutes remaining
We’re always looking for clever ways to be more efficient in our coding, especially when reusing code between projects or creating more repeatable style patterns. If you want your code to be more readily reusable between projects, we happily invite you to discover an improved way to use custom CSS properties.
Custom CSS properties let us define our own reusable values in our stylesheets. They don’t do much independently, but they can be plugged into other styles and functions where needed. They’re super flexible! They can store a wide range of values, a list of values, expressions, and even reference other custom properties. The most common pattern is using them to define global values on :root or html to reference throughout your code:
That said, custom properties can be defined on any selector like other CSS properties. They can be limited to specific selectors, and we can even control whether they cascade and child elements inherit them thanks to @property. For those of us familiar with Sass, they may seem like they’re equivalent to Sass variables, but there are some key differences.
Sass variables can be used anywhere as just a string replacement in the CSS with interpolation. We can use them for global values, in media queries, and in selectors:
$color-lava: #dc323d;
@media screen and (min-width: $md) {
#{$btn}__icon {
Custom CSS properties can’t be used in media queries or selectors, but unlike Sass, they are evaluated by the browser. They don’t require a build step and can change values based on browser state, like prefers-color-scheme queries and state changes like hover.
Most importantly, though, they can change values based on the cascade, meaning their values can change based on parent elements, follow the specificity rules with selectors, and are evaluated for each element on the page.
Let’s look at an example of how we can use this to make it easier to change the appearance of elements on a website. We’ll start by defining the custom property --accent-color.
:root {
--accent-color: blueviolet;
}
Now let’s write the CSS for a link button, but instead of directly referencing any specific colors, let’s just reference --accent-color.
Great! Now let’s do the same for a simple message:
.message {
color: var(--accent-color);
border-radius: .25em;
padding: 1em 2em;
background:
rgba(from var(--accent-color) r g b / .1);
box-shadow: 0 .5em 1em
color-mix(in srgb, var(--accent-color), #0001 90%);
}
Besides being able to use --accent-color for the text color, we can use the custom property with other new CSS functionality like rgba(from …) and relative colors. Here we’ll just set the opacity to .1 – or color-mix(), which we can use to fade and darken the drop shadow.
We can use --accent-color again here. There’s no need to define a property just for messages. We can also use .warning and .success again - The new .message styles will just work with the existing accent color classes:
<div class="message">
<strong>Hey!</strong> I hope you are enjoying the article you're reading.
</div>
<div class="message warning">
<strong>Uh oh</strong> - I'd be careful if I were you.
</div>
<div class="message success">
<strong>Good job,</strong> you really did that well.
</div>
Let’s tackle one more problem while we’re at it—text color accessibility. Using our button example again, let’s say we have another class, .alert, that sets --accent-color to a yellow:
.alert {
--accent-color: yellow;
}
Completely unreadable. We need to fix .button to change the text color based on the accent-color. Unfortunately, having a simple CSS function for this was bumped to the Level 6 draft, but we can implement a poor man’s version with relative colors. Instead of implementing it solely for .button, let’s resolve it in a way that’ll work everywhere:
We can define another custom property, --accent-color-contrast, with a formula that will either return white or black based on the lightness of --accent-color. Untilcontrast-color() shows up to save the day, this scrappy workaround will get the job done. Now, when we update the .button color to use the new property instead of setting it to the default (white ), the text color dynamically changes based on the accent color.
This pattern—building components based on custom properties, and utility classes for setting those properties—keeps the code organized and easy to update and maintain. Code written this way is more reusable between projects and makes a great starting point for new projects, which is why we’re adopting this approach in our front-end component library, Pronto. We’re currently working on our second iteration, so stay tuned for more exciting and clever enhancements to Pronto!
Would you like to talk about how CSS properties could help make your website stronger? Say hello!