Individual addressability of CSS things

One of the things that I find valuable about CSS, but is being considered a little less often these days, is individual addressability of parts of a CSS declaration.

An example. You have an element, with a background:

p#myelement1 { 
  background: url(http://kryogenix.org/images/hackergotchi-simpler.png) top left repeat-x;
  min-height: 100px;
}

and then at run time you want to change the background image to something else. Having to set the whole background property would be really irritating here: how would you do it? You’d have to do something horrible like read the whole background property and then perform a textual .replace() on it in JavaScript. Fortunately, the CSS people thought of that: a property like background is actually also broken up into a zillion smaller properties: background-image, background-size, background-position, background-repeat and so on. Changing the image (but not changing the remainder of the property) is now easy: just set myElement.style.backgroundImage from JavaScript, and Robert’s your mother’s brother. Done.

This neat feature of CSS, of being able to set not only the totality of a property but also all its parts individually, seems to be being weakened by more advanced properties (ones where the value is not just something primitive but is instead its own tiny domain-specific language). Take, for example, gradients.

p#myelement2 { 
  background: linear-gradient(to bottom, #1e5799 0%,#7db9e8 100%);
  min-height: 100px;
}

[raw]

[/raw]

Here we have a gradient, from darkish blue to lightish blue. I would like this gradient to change to be darkish red to the same lightish blue. In order to make that change at runtime, I have to change the whole background-image property. I can’t just change the first colour stop and not the rest. In essence, this means one of two approaches if I want to change this property at run time.

1. The first is that I have to hardcode my CSS into my JavaScript as well. So my JS has, written into it, that the second colour stop is #7db9e8 100%, and I say

myElement.style.backgroundImage = "linear-gradient(to bottom, " + myDarkishRedColour + " 0%,#7db9e8 100%)";

This is hopelessly irritating. It almost wholly defeats the point of having a CSS stylesheet; I can’t change anything in the CSS without also checking whether the thing I’ve changed is in my JS somewhere too as well.

2. So then there’s the second approach, which is that I read the existing value of the property and parse it myself, and just change the bit I want. In other words, that I write a CSS parser in JavaScript. This is an unbrilliant suggestion. It is so unbrilliant that other stupid ideas come and pray at the foot of it. We did this once — this is what Sizzle was for CSS selectors — and then document.querySelector came along and obsoleted it, because the browser already has to know how to parse this stuff and can do it more reliably and faster, so it should expose that to JS.

Obviously, if I only ever want to change from dark-blue-gradient to dark-red-gradient, I just define a class and set the class from JavaScript. However, if I’m doing anything dynamic at all, this doesn’t work. Well, perhaps it does, because there is…

3. dynamically add stuff to my stylesheet and then use it. So I add a new selector for p#myelement2.something to the stylesheet at runtime and then set the something class on the element. But this does not help: I still need to parse or hardcode the values in the gradient!

Class names used to be like this in JavaScript. If you wanted to add class="happy" to an element, you couldn’t just say element.className = "happy" because that element might already have a class and you’d wipe it out. So you had to do element.className += " happy", and then removing classes became a complex little regexp exercise with \b escapes for end of word… and then element.classList came to save us, and now each class is individually addressable.

Making things individually addressable does lead to a large proliferation of properties: border-radius is actually border-bottom-left-radius and border-top-right-radius and border-top-left-radius and border-bottom-right-radius. But it works; when you need it, the individual properties are there, and when you don’t, you can just set the aggregate property and not think about the hordes on which its built.

I wish this happened for new things in CSS. Changing the second background-size in a multi-valued background-size, or just the higher-DPI image in a responsive image, or one of the colour stops in a gradient. Individual addressability of things is a lovely property of CSS, and we shouldn’t just abandon it.

I'm currently available for hire, to help you plan, architect, and build new systems, and for technical writing and articles. You can take a look at some projects I've worked on and some of my writing. If you'd like to talk about your upcoming project, do get in touch.

More in the discussion (powered by webmentions)

  • Stuart Langridge responded at twitter.com @sil @rachelandrew @meyerweb so, how do I change that 7em to 8em from JS? Do I have to set the whole CSS value, or can I just set the 7em?
  • David Moulton responded at tag:twitter.... (twitter.com)
  • Surma responded at twitter.com @sil Lots of types still missing (e.g. timing functions). Will come eventually.
  • Stuart Langridge responded at twitter.com @DasSurma just checking I hadn't missed it! Looking forward to that :)
  • (((вкαя∂εℓℓ))) responded at tag:twitter.... (twitter.com)
  • Aimee Knight responded at tag:twitter.... (twitter.com)
  • Jakob 💈 responded at twitter.com
  • Ana Tudor 🐯 responded at twitter.com @Rumyra Explanation of Typed OM from @rumyra, which is about individual addressability of CSS properties (and other things): set CSS bits di
  • вкαя∂εℓℓ responded at twitter.com