The recent addition to CSS of Custom Properties, a.k.a. CSS variables, was a very welcome addition to the language. It's opened the door for a lot of really cool techniques; there's a lot that you can only do when you can keep track of the state of your system and keep different bits in sync with each other, and variables give you a way to do that.
But here's something that developers newer to CSS may not know, and those who've been around often forget: we've actually had another variable in CSS since long before Custom Properties came on the scene. It's much simpler, and limited in scope, so it can't do everything Custom Properties can do, but it's way better supported, and with a bit of cleverness it can be used to accomplish some pretty cool stuff.
This variable is the special CSS keyword currentColor
!
What is it?
The currentColor
keyword is basically what it sounds like: it's a special keyword that holds the value of the color
property at the location where it's used.
So for a very simple example, you can use it to make a pull quote box with a border that's the same color as the text in the box:
blockquote {
color: hotpink;
border-color: currentColor;
}
Snazzy, I think.
Interestingly though, in this particular case, the border-color
rule is unnecessary. I always thought that the default border-color
value was black
, or else was set by the browser, but I recently discovered by accident that it actually defaults to currentColor
! So if border-color
just isn't specified, it will use hotpink
by default!
Several properties actually default to currentColor
:
- border-color
- caret-color
- column-rule-color
- text-decoration-color
- text-emphasis-color
-
outline-color (sometimes; read the link and read about the
invert
keyword, which is used if it's supported)
By the way, I didn't know about almost any of those rules before I looked up which rules default to currentColor
! I need to read more about text-emphasis
; it's really weird and interesting. Man, every time I dive into the docs to find something specific, I end up learning about something unexpected.
Browser support
Of course, the first question you should ask whenever you learn about a cool feature of the web platform that you've never seen before is, "How good is its browser support? Can I actually use it?" Fortunately, the answers for currentColor
are "super good" and "yes!"
Support actually goes back a really long time. Taking a look at caniuse.com's overview of support, it's supported in every version of Edge, it's been in Chrome and Safari since version 4 (back then they used the same engine under the hood), and it's been in Firefox since version 2! Heck, it's even in Internet Explorer all the way back to IE 9! So don't worry about using it unless you're writing an intranet application for an organization that still hasn't upgraded past IE 8. (Tragically, I'm very aware that they still exist π’)
Demo: Badges
Okay, let's get practical.
I discovered currentColor
while investigating the best way to implement a design for work. I needed to create a row of colored badges, each with a label on the badge and a numeric counter next to it. The background color of each badge matched the text color of its counter, and the label on each badge was colored to match the site background. For the sake of example, suppose the mockup was something like this:
Each of those badges is identical, but for the color. Seems like a good use case for a reusable component! In a modern browser, you could use a CSS variable for the background of the badge and the foreground text color of its counter. But suppose you want/need to support IE 11, Edge 12-14, or another browser that doesn't support custom properties. Or maybe you already have some utility classes in your CSS to define text colors for various situations, like Bootstrap's contextual color classes, e.g. .text-danger
and .text-warning
, so the idea of using the color
property to set the badge color is super convenient. How might you go about it, armed with your new knowledge of currentColor
?
First we'll need a root wrapper element for the component. This is where the color will be set, and it's where you'd add a class to override the color.
<div class="badge">
</div>
.badge {
display: inline-block;
color: cornflowerblue;
}
So what needs to be inside the badge component? Well, at first glance there are two regions: the bit with a colored background and white text, which I'll call the title block, and the digits off to the side that are colored to match the title block's background, which we've been calling the counter.
The title block definitely needs its own block in the DOM, since it has a background color, but the counter probably doesn't, since it's just sitting off to the side, which is what inline text does anyway. So lets try this markup:
<div class="badge">
<div class="title-block">
Things
</div>
12345
</div>
Now the fun part! We want the background of the .title-block
to match the color
specified on the .badge
, so we can use background: currentColor
! We also want to set the .title-block
's text to white, according to our mockup, so let's add color: white
as well. (If you've spotted the problem here, no spoilers!)
.title-block {
display: inline-block;
padding: 0.5rem;
background: currentColor;
color: white;
}
Great! Let's see how it looks:
Ah. Well. That's not what we wanted at all. The title block seems to be missing... or rather, it's completely white. Why is that?
Well, here's the thing: CSS is a declarative language, not an imperative one: it doesn't run top-to-bottom, keeping and updating state along the way. What this means is that when we write:
.something {
background: currentColor;
color: white;
}
the browser doesn't figure out currentColor
's value, then set color: white
. Instead, setting color: white
changes the value of currentColor
for the background to white
as well! This is a limitation of currentColor
that you need to keep in mind.
It's an easy enough fix, if a little annoying. We just need another layer of nesting to encapsulate the color: white
rule. CSS rules often inherit downward to children, but never upward to parents, so this lets .title-block
inherit its currentColor
value from .badge
, while being able to change the color
within its child.
<div class="badge">
<div class="title-block">
<span class="title-block-text">
Things
</span>
</div>
12345
</div>
.title-block {
display: inline-block;
padding: 0.5rem;
background: currentColor;
}
.title-block-text {
color: white;
}
Now let's take another look:
Perfect!
Now that we've got our component, all we need to do is repeat it a few times and define a color-override class for each be instance.
<div class="error badge">
<div class="title-block">
<span class="title-block-text">
Errors
</span>
</div>
123
</div>
<div class="warning badge">
<div class="title-block">
<span class="title-block-text">
Warnings
</span>
</div>
45
</div>
<div class="success badge">
<div class="title-block">
<span class="title-block-text">
Successes
</span>
</div>
12345
</div>
.error {
color: red;
}
.warning {
color: goldenrod;
}
.success {
color: green;
}
Awesome! We met the requirement!
And this little badge component is pretty handy; there's still more to play with. We can try different colors, put emoji in the title area, add a background color to the counter area... Lots of possibilities!
What else can you come up with?
Conclusion
To recap: there's a cool, undervalued keyword in CSS, currentColor
, that can be used as a variable of sorts for certain use cases. It has much more broad support than CSS Custom Properties (a.k.a. CSS variables), and it is perhaps a bit more intuitive to use when developing a reusable component, since you only need to set the color
property to populate it's value, which feels very nice.
However, it's clear that currentColor
has limitations. The most glaring when comparing to Custom Properties is that it's only a color value, so it can't be used to store length values, image URLs, or fancy gradients. The other main point of caution is that you can't use an inherited color
value in currentColor
, say for the background or border color, while also setting your own color
for text in the same block; setting color
will update the value of currentColor
everywhere within that block. You need to introduce a child element to keep your color
value in a new scope.
So in short, it has limits and caveats, but I personally have found currentColor
to be very useful, and it has let me write much cleaner code in a few tricky situations. My absolute favorite feature is just being able to set color
on a root element of a component and see the whole thing update. Give it a try, see what you can do with it!
Top comments (7)
Thanks for writing this up, this is really interesting! I love deep dives like this into things I've never really paid much attention to.
I also really like how you walk through the steps of how you'd expect things to work, which includes running into issues and figuring out what you need to fix to make it work. It makes it much easier to understand how things work, and why you might need to write things a specific way.
Looking forward to future posts!
Thanks! π I usually find that working through weird errors and unexpected behaviors is the best way to really learn the details of a feature, so I like to show that. I'm glad someone else appreciates the approach!
My favorite use of currentColor is setting an outline (or border) color on anchors in focus. A quick win to improve keyboard accessibility. For example: a:focus {outline: 3px dotted currentColor;}. Cheers!
Awesome article! One thing bothers me (a lot): it should be called
current-color
like everything else in CSSHa, I know what you mean, it does seem a little inconsistent. But it actually follows the standard pattern for color names, like "MediumVioletRed", "DarkOliveGreen", or "RebeccaPurple". CSS treats these names case-insensitively, so I actually write all lowercase
currentcolor
most of the time.Same.
interesting! I wasn't aware this is something from CSS. Thanks for digging and sharing it with us.