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.