Programming CSS
There’s a worrying tendency for “real” programmers look down their noses at CSS. It’s just a declarative language, they point out, not a fully-featured programming language. Heck, it isn’t even a scripting language.
That may be true, but that doesn’t mean that CSS isn’t powerful. It’s just powerful in different ways to traditional languages.
Take CSS selectors, for example. At the most basic level, they work like conditional statments. Here’s a standard if
statement:
if (condition) {
// code here
}
The condition needs to evaluate to true
in order for the code in the curly braces to be executed. Sound familiar?
condition {
// styles here
}
That’s a very simple mapping, but what if the conditional statement is more complicated?
if (condition1 && condition2) {
// code here
}
Well, that’s what the decendant selector does:
condition1 condition2 {
// styles here
}
In fact, we can get even more specific than that by using the child combinator, the sibling combinator, and the adjacent sibling combinator:
condition1 > condition2
condition1 ~ condition2
condition2 + condition2
AND is just one part of Boolean logic. There’s also OR:
if (condition1 || condition2) {
// code here
}
In CSS, we use commas:
condition1, condition2 {
// styles here
}
We’ve even got the :not()
pseudo-class to complete the set of Boolean possibilities. Once you add quantity queries into the mix, made possible by :nth-child
and its ilk, CSS starts to look Turing complete. I’ve seen people build state machines using the adjacent sibling combinator and the :checked
pseudo-class.
Anyway, my point here is that CSS selectors are really powerful. And yet, quite often we deliberately choose not to use that power. The entire raison d’être for OOCSS, BEM, and Smacss is to deliberately limit the power of selectors, restricting them to class selectors only.
On the face of it, this might seem like an odd choice. After all, we wouldn’t deliberately limit ourselves to a subset of a programming language, would we?
We would and we do. That’s what templating languages are for. Whether it’s PHP’s Smarty or Twig, or JavaScript’s Mustache, Nunjucks, or Handlebars, they all work by providing a deliberately small subset of features. Some pride themselves on being logic-less. If you find yourself trying to do something that the templating language doesn’t provide, that’s a good sign that you shouldn’t be trying to do it in the template at all; it should be in the controller.
So templating languages exist to enforce simplicity and ensure that the complexity happens somewhere else. It’s a similar story with BEM et al. If you find you can’t select something in the CSS, that’s a sign that you probably need to add another class name to the HTML. The complexity is confined to the markup in order to keep the CSS more straightforward, modular, and maintainable.
But let’s not forget that that’s a choice. It’s not that CSS in inherently incapable of executing complex conditions. Quite the opposite. It’s precisely because CSS selectors (and the cascade) are so powerful that we choose to put guard rails in place.