Custom properties
Custom properties are a way that we can store values, and reuse them throughout a code base. You’ll often hear them called CSS variables as well as custom props.
Custom properties are one of the most powerful features of CSS. For this lesson, we’re only going to be taking a quick look at how they work so that you’re familiar with the syntax, and look at the simplest way to use them (which still have huge benefits!).
As we progress through this course, we’ll explore other use cases for them.
The syntax
Section titled “The syntax”Declaring a custom property
Section titled “Declaring a custom property”To create a custom property, we must name that property with a double hyphen (--) at the start. This is to ensure there are no naming collisions between your own properties, and existing ones in CSS, or future properties that are added later on.
--my-custom-property: a-value;Like any other property:value pair, we must declare a custom property as part of a CSS rule.
/* this wouldn't work */--brand-color: deeppink;
/* this would work */html { --brand-color: deeppink;}Using a custom property
Section titled “Using a custom property”To use a custom property, we have to use the var() function.
.avatar { --color: hsl(200 75% 50%);
border: 3px solid var(--color); box-shadow: 0 0 20px var(--color); /* other styles */}See the Pen simple use of custom props by Kevin (@kevinpowell) on CodePen.
Custom properties are inherited
Section titled “Custom properties are inherited”Like our content styling properties, custom properties are also inherited, which means we can declare them once and reuse them over and over.
html { --brand-color: deeppink;}
a { color: var(--brand-color);}
blockquote { border-left: 3px solid var(--brand-color);}What the heck is :root?
Section titled “What the heck is :root?”If you have seen custom properties before, you’ll have most probably seen them declared in the :root selector.
:root { --brand-color: hotpink; --text-color-base: #333; --text-color-high-contrast: #111; --text-color-inverse: #ddd; --background-color-base: #ebebeb; --background-color-inverse: #222; /* etc */}The :root selector is a pseudo-class that selects the html element. You could replace :root with html and you would have the same result.
The big advantage of custom properties is that they are inherited, allowing us to declare a value for them once, and reuse that over and over again in our project, so it makes sense for them to be declared on the html element.
If they’re essentially the same, why bother :root? One reason early on that I heard quite often is that :root, being a pseudo-class, has higher specificity. This is true, but I never ran into a practical reason that this made any difference.
However, if for no other reason, it’s practical because you know where they will be in every project. It’s become a standard that you’ll see everywhere, and for me, it separates my variables from regular styles I might apply to the <html> element itself.
Common use cases
Section titled “Common use cases”Any values that you might repeat more than once become useful to have as custom properties, with the two most obvious examples being:
- colors
- font sizes
Other common use cases include spacing/sizing, effects like shadows and gradients, and timing functions.
One of the big advantages of using custom properties for these is you no longer have to remember that your brand color is a relatively random string of letters and numbers, you just have to remember --brand-color.
On top of that, if you ever need to change a color, we only need to do it once and it updates everywhere, which is a huge win (and makes theming very easy).
Custom props and the cascade
Section titled “Custom props and the cascade”Custom properties work very similar to other inherited properties, in that we can easily overwrite them.
:root { --text-color: #efefef; --surface-color: #323232;}
.inverted-color-scheme { --text-color: #efefef; --surface-color: #323232;}See the Pen Custom props and the cascade by Kevin (@kevinpowell) on CodePen.
Locally scoped custom properties serve a purpose too
Section titled “Locally scoped custom properties serve a purpose too”Beyond redeclaring the value of custom properties, there are times where you might want to use locally scoped custom properties, which is when we declare a custom property on a specific selector instead of inside the :root.
We’ll come across some of those use cases later on, I just want to mention for now that there is no rule that every custom property must be on the :root selector.
@property
Section titled “@property”This is getting a bit more advanced, so I don’t want to get into the use cases for this feature now, but I do want to mention that it exists.
We can register a custom property using @property instead of declaring one inside of a rule. Doing this allows the values of custom properties to be animated, which can open up some interesting possibilities, and it also allows us to turn off the inheritance of a given property. It is much more verbose to register them, and I’d generally suggest only doing so for situations where you need to.
@property --gradient-value-1 { syntax: "<color>"; inherits: true; initial-value: hsl(200 50% 50%);}