Using CSS custom properties (aka variables) is a great way to create themes, but it also enables what’s IMHO quite possibly one of the only valid use cases for inline
style attributes, as it brings the following advantages:
- it’s SSR compatible out of the box
- it doesn’t affect the native look and feel until the theme is loaded
- it doesn’t affect the layout if, for whatever reason, the external CSS resource failed due bad network conditions, or even CDN issues
What’s being covered in here, is a technique to localize custom properties that plays well with inheritance, as properties, like anything else, can affect inner nodes, but it doesn’t have to be like that.
CSS properties in action
Let’s take the following case as example:
--background-color: rgba(150, 150, 255, .5);
If we use the
background-color: var(--background-color, initial); approach, which “suffer” inheritance, we’ll see two different stripes.
However, if we use the locally scoped property/variable approach, the nested
<div> would not be affected by its outer container.
How does it work?
It’s pretty simple: every target node will have its own property, which will have higher priority over the one defined in any outer component, granting a possible default behavior, hence portable for literally every property we need, including
border, and so on and so forth.
local VS global
The “local” version of this technique is
!important proof: no matter if the outer container used
!important, the inner
<div> won’t be affected.
This makes such technique an ideal companion for Custom Elements, without needing to use any Shadow DOM to ensure styles are applied as desired.
On the other hand, the “global” approach can be overruled via
!important, either via the external file or through the inline
The “global” approach though, is more suitable for generic rules, as example having a consistent
border-radius: var(--border-radius, initial) that would rarely need to change, and the same goes for
font-family, still providing the ability to define, within a specific element, a different kind of property.
color: var(--color, silver);
With this technique, all it takes to change color to every
<div class="darker"> and every possible nested div, is to change the
--color property of the darker, so that all others will inherit such property without falling back to
These techniques can be combined with classes too, and the only change to our daily CSS code is that we need to think in variables.
Only those very specific cases where we want different defaults, or animate some property via JS, could use
node.style.cssText = '--height: 200px';, as example, still without affecting elements, if the “local” approach is used.
In short, it’s a new approach to CSS variables that shouldn’t be abused, but can deliver great achievements, and frankly simplify most custom/ad-hoc changes for this or that element, without forcing us to create thousand classes with names rarely as semantic as an explicit
What about IE?
The day we’ll stop asking this question will be a great win for the Web. However, there are at least 4ways to solve the IE issue:
- do nothing, as the technique is graceful enhancement out of the box, so that old IE will simply look natively OKish
- transform the modern CSS into IE compatible CSS, loosing the ability to change, or define variables at runtime, but IE is not good with animations anyway (preferred method)
- use this polyfill for IE11 and call it a day (also suggested)
- define properties before specifying variables, so that modern browsers will be using variables, while IE will ignore the rest
color: var(--color, silver) !important;
Once again, these examples are not suggesting what you should really do in production, are simply demoing the approach with both modern and older browsers.