HomeBlog

Using CSS Vars to Colour Objects

MCMWednesday, March 17, 2021

While I was building Blockville out of SCSS and bloody Typescript, I encountered a whole host of issues, but the one that required the most reworking was the question of colouring the buildings themselves. Take a look:

When I started, I had a set of 10 colours that were randomly assigned to each building, and the buildings themselves were just flat blocks at funny angles — no shading. At first, I convinced myself that was a creative choice, and that I preferred the simplistic effect. I wasn't lazy, I had a vision!

But then I started adding fancy stuff to the roofs, and suddenly the issue of colour came back to the fore. Each one of those little doodads is its own block of divs, customized to behave a certain way.

The issue was: if the colour was randomly-assigned to the parent object, how was I going to apply it to the child objects too? It's one thing to assign a CSS value to a single element with javascript, but crawling down the tree to hit the kids, too? Yikes.

(side note: that sentence sounds very wrong)

The answer was CSS vars. If you don't know them, you define them like this:

--thisVar: 10px

and then use them like this:

padding: var(--thisVar);

Pretty simple, right? Define some key vars up top, and use 'em with reckless abandon. It's like Sass, but at runtime! But how does that help with the buildings? By tapping into one of my favourite things:

Inheritance!

Here's the wonderful thing about CSS vars: they are available to all children of their parent object. So, for example, if we did this:

.parent {
    --thisVar: 10px;
    padding: var(--thisVar);

    .child {
        padding: var(--thisVar);
    }
}

Then the parent and the child will both have 10px padding. The inheritance carries on until you override it somewhere down the tree (if you are so inclined).

Now in terms of Blockville, this lets me do the following bit of magic (psuedo-code follows for clarity):

.building {
    --bgCol: #ff0000;

    .wall-left, .wall-right {
        background-color: var(--bgCol);
    }

    .roof {
        background-color: var(--bgCol);
    }
}

And now all these pieces are using the same colour as the root colour. Swap that first variable, and the rest automatically update. And since in Blockville, there are lots of fiddly little pieces on each building that may or may not be the base colour, this allows me to target exactly what I want without using JS to do the updating.

And that's the best part...

Colourizing with reckless abandon

What the CSS does is allow us to set all colours in a building at once. But how do we set that colour in the first place? Easy!

document.getElementById('building1').style.setProperty(`--bgCol`, '#00ff00');

Now that we've redefined --bgCol to green, the whole building will change accordingly. Abstract that out a bit:

const colours = [ '#ff0000', '#00ff00', '#0000ff' etc];

for (i = 0; i < 16; i++) { 
    document.getElementById(`building${i}`).style.setProperty('--bgCol', colours[i]);
}

...and now you've got a bunch of buildings with their base colours defined at runtime — either from a list, or randomly, or (in Blockville's case) derived from user input.

Seems simple and obvious, but it took me about 900 years to figure it out. Don't judge me, I was tired.

All content released under a Creative Commons BY-NC license except the contents of "TV" section, which belong to their respective owners.