kapowaz

October 21 2013

Cargo Cult CSS

It is generally accepted that having a methodology for writing and managing CSS is preferable to having none at all. In spite of this, some of the practices developers have adopted are having a detrimental effect on the semantic quality and longterm maintainability of what we build. I’m going to talk about some of the problems with techniques being advocated by CSS ‘framework methodologies’ and how we, as web developers, can better solve these problems.

The most popular framework methodology for developing CSS today is OOCSS, although other similar techniques exist, such as BEM. These methodologies attempt to apply the principles of Object-Oriented Programming to CSS. Notwithstanding the conceptual incompatibilities between a declarative style language and object-oriented software design principles, these methodologies introduce subtle problems which may not be immediately obvious to less-experienced developers. Most disconcertingly, these methodologies have seen widespread adoption thanks to prominent bloggers evangelising their usage as ‘best practice’. The abscence of evidence to support the claimed benefits of using these methodologies — outside of a very small, select group of high traffic sites — reflects my view that they represent a misleading and harmful cargo cult.

Semantics

There are only two hard things in Computer Science: cache invalidation and naming things.

Phil Karlton1

The web is fundamentally a semantic medium. This is a crucial feature of a platform intended to be accessible to people of many different languages, cultures, genders, ages, physical and cognitive abilities, through a tremendous array of different types of technologies. There is no single vision of what the web should be or look like, and unless you’re building something with an explicitly narrow scope, a semantic approach should be at the core of everything you do as a web developer.

When writing HTML, there are three primary ways of expressing the semantics of the content or interface of a website or application: the type of elements you markup your content with; the ID you use to identify a distinct, individual element; and the class names you use to categorise a set of elements.

Of these three, class names are the most commonly-used tool for binding presentation to a HTML document. It’s important to note that when discussing class names, the W3C’s most recent candidate recommendation for HTML 5 says:

There are no additional restrictions on the tokens authors can use in the class attribute, but authors are encouraged to use values that describe the nature of the content, rather than values that describe the desired presentation of the content. 2

Nowhere in the specification of the class attribute is it stated what purpose the attribute serves other than the recommendation that it be used to describe content semantics. Despite this, it has become very common for web developers to incorrectly refer to the class attribute as the ‘CSS class’, or similar. As Tantek Çelik notes:

By using the phrases “CSS class(es)” or “CSS class name(s)” you’re not only being imprecise (or just plain wrong), you’re tying the presentational context/framing of “CSS” to class names which implies and even encourages the bad practice of using presentational class names. 3

As this recommendation is completely incompatible with framework methodologies, they choose to disregard it. Since class names in this type of project must describe their presentation, they are inherently non-semantic — despite protests to the contrary. But why do semantic class names even matter? At the most basic level it’s about maintaining the platform-agnostic, inclusive design principle that Tim Berners-Lee described as ‘the universality of the web’. But it’s also because we cannot predict the ways in which future innovations in technology may use what we’re building today.

One example of this is Microformats. The only reason these came into being is because document authors had used semantic class names to describe common structures such as addresses or calendar appointments. By codifying certain conventions used to describe data in this way, the markup became useful not only to website visitors, but also to software capable of understanding and using it in new and unimagined ways. Microformats demonstrate the usefulness of semantic class names, so it’s disappointing to see them being dismissed:

Class names impart little or no useful semantic information to machines or human visitors unless it is part of a small set of agreed upon (and machine readable) names – Microformats.

Nicolas Gallagher 4

This arbitrary delineation between the ‘small set of agreed upon names’ and all the potential, future uses of content being built and published today is driven purely by the need to justify the CSS framework ideology. By mandating non-semantic class names, the structure and content of the document are being dictated by the way that content is being presented, which is a clear violation of the separation of concerns.

Maintainability

When building websites, we must always ensure that what we produce is forward-thinking enough to be easily maintained and adapted throughout the lifespan of the product. Modern websites and applications are increasingly the work of large teams of front-end developers. In this environment, it is extremely important that the methodology used to author CSS can be successfully adopted across the entire team, some of whom may only work on the product during phases of its lifespan, or who may not have been there at the product’s inception. Many challenges related to implementing CSS can arise in this environment, including:

Perhaps the most significant driving purpose behind a CSS framework methodology is to empower front-end developers to solve the first of these, the duplication of code across a site’s CSS. By using short, class name-derived selectors, a developer can increase the utility of the selector, and share its rules across a multitude of elements. Consider the following example class, which gives any matching element consistent border style, padding and typography:

 1.box-standard {
 2  color: blue;
 3  border: 2px solid blue;
 4  border-radius: 5px;
 5  padding: 20px;
 6  font-family: Helvetica, Arial, sans-serif;
 7  font-weight: normal;
 8  font-size: 1rem;
 9  line-height: 1.4;
10}

A developer might then apply this class to any number of elements, all of which would take this style. This may include other elements which don’t use all of the styles, but instead override particular properties as they need, by declaring another class further down in the source CSS. As both classes have the same specificity, the latter declaration would take precedence:

1.box-special {
2  color: red;
3  border-color: red;
4  font-weight: bold;
5}
1<div class="box-standard box-special">
2  Here is a special box!
3</div>

While reducing the duplication of code is desirable, by implementing classes in this fashion the markup is forever tied to the presence of both classes, neither of which necessarily conveys any particular semantic meaning.

What the developer really needs here is a combination of mixins and extensions, which exist in various CSS preprocessor tools, such as LESS or SASS. A collection of related properties can be grouped as either a mixin or an @extend placeholder, which can then be given an entirely presentational name at no cost: it doesn’t surface itself anywhere in the generated code (unless you want it to, for debugging purposes) and so it can lend its rules to any arbitrary CSS selector. Here’s a simple example of a mixin in the SCSS syntax:

 1@mixin news-item($color) {
 2  border: 2px solid $color;
 3  border-radius: 5px;
 4  padding: 20px;
 5  font-family: Helvetica, Arial, sans-serif;
 6  font-weight: normal;
 7  font-size: 1rem;
 8  line-height: 1.4;
 9}
10
11div.news {
12  @include news-item(blue);
13}
14
15div.breaking {
16  @include news-item(red);
17  font-weight: bold;
18}
1<div class="news">
2  Here is a news item.
3</div>
4
5<div class="breaking">
6  Here is a breaking news item!
7</div>

Changes to the mixin’s style declarations will always filter through to the selector div.breaking, and the markup is as loosely-coupled as it can possibly be. The ability to define mixins natively in CSS is a sorely-missed feature (which thankfully is finally in the process of being formalised), but if you’re writing CSS professionally then there’s really no good excuse for not using a CSS preprocessor.

This loose coupling, or separation of concerns is crucially important to the future maintenance of the project; coupling markup and CSS too closely can increase the future cost of modifications. In the earliest days of CSS’s viability, the proliferation of WYSIWYG web development tools like Dreamweaver and Frontpage led to a lot of websites with code that looked a lot like this:

1<span style="display: block; font-family: Arial, sans-serif; font-size: 11px; color: blue; border-style: solid; border-color: blue; border-width: 2px; padding: 20px;">Here’s a box.</span>
2<span style="display: block; font-family: Arial, sans-serif; font-size: 11px; color: blue; border-style: solid; border-color: blue; border-width: 2px; padding: 20px;">Here’s another box.</span>
3<span style="display: block; font-family: Arial, sans-serif; font-size: 11px; color: blue; border-style: solid; border-color: blue; border-width: 2px; padding: 20px;">Here’s yet another box. Noticing a trend yet…?</span>

This kind of inline style-based markup was only a modest progression from the ‘tag soup’ that blighted the web in the late ‘90s. Whenever the design needed to be modified, all instances of these inline styles would need to be tracked down and replaced. After escaping this kind of development hell it saddens me to see that the lessons of those days appear to have been forgotten as we can still encounter code that looks like this:

1<span class="display-block blue-box font-arial color-blue solid-blue-border padding-20">Party like it’s 1999!</span>
2<span class="display-block blue-box font-arial color-blue solid-blue-border padding-20">Hey, have you checked K10K.net lately?</span>
3<span class="display-block blue-box font-arial color-blue solid-blue-border padding-20">Wassuuuuuuup!</span>

This might seem like an extreme example, but it’s the logical conclusion of a CSS framework methodology that abandons semantic class names and selectors in the pursuit of ‘modularisation’. While avoiding duplication of code within the CSS, it is simply transferred to the markup, incurring close-coupling side-effects in the process.

One of the other goals of this methodology is delivering high performance. This depends on many factors, such as the kind of client visiting a web page, the speed of their connection and whether content has previously been cached, amongst other factors. At the basic level there are three things which most demonstrably impact CSS performance:

The first two of these are considered out of scope for a CSS framework methodology, whilst the third is addressed through class name re-use: the intention is that by declaring as few style properties as possible, the CSS is already as small as it could conceivably be.

Delivering such a CSS document is potentially misguided, however: by using smaller selectors with fewer rules, and moving more of the bindings across to the markup (with more, and longer class names), the amount of data being transferred isn’t necessarily going down, but it does move more of it out of the CSS file — usually aggressively cached and delivered by a CDN — and into the document, which may not be cached at all.

Keeping CSS selectors short is often cited by proponents of CSS framework methodologies as having performance benefits. This has since been disproven as modern browser engines are now efficient enough that in all but the most extreme edge cases, the speed gains possible from rewriting your selectors are negligible. Whilst front-end performance is definitely worth pursuing:

What many people don’t seem to realize (or won’t talk about) is that performance often comes with a very real and tangible cost: it eats away on maintenance.

Niels Matthijs 5

Some of the most serious problems with the CSS framework methodology are exposed when revisiting older code, or when a new team member joins a project. This is principally a result of these approaches providing almost no discoverability. In his article on front-end architecture, Nicolas Gallagher says that “Class names should communicate useful information to developers”. I’d take this one step further: selectors should communicate useful information to developers. The purpose is the same — let me understand where this rule is used — but a selector is far more informative to the developer, as it communicates context. It’s a lot easier to understand the purpose behind a rule if the selector tells you that it only applies to elements within a certain scope. Consider two possible ways of styling a link within a list of names of members of a society:

1.list-link {
2  font-weight: bold;
3  text-decoration: none;
4}

or:

1ul.members li a {
2  font-weight: bold;
3  text-decoration: none;
4}

The latter is more readily understood by somebody who is unfamiliar with the codebase, be that because they’re completely new to it, or simply because they’ve not worked on it recently. It’s also inherently more discoverable: a new developer can have no way of knowing if suitable rules already exist without reading (and memorising) the entire CSS codebase. This would be an unrealistic and impractical expectation to place upon a developer and in all likelihood they’ll simply create another rule that does the same thing, completely undermining the goal of reduced repetition.

Elegant code is not only correct but visibly, transparently correct. It does not merely communicate an algorithm to a computer, but also conveys insight and assurance to the mind of a human that reads it. By seeking elegance in our code, we build better code. Learning to write transparent code is a first, long step toward learning how to write elegant code — and taking care to make code discoverable helps us learn how to make it transparent. Elegant code is both transparent and discoverable.

Eric S. Raymond 6

Related to the problems of discoverability, the framework methodology also lays the seeds of software bloat. By writing selectors to be specific, you also give developers the necessary information to determine once they’ve become redundant. Using the above example, if the society member list is removed from the website, the entire block of CSS can be deleted, whereas flexible class name-based selectors may conceivably be used elsewhere, and without a more serious clean-up effort it might not be immediately apparent if the rule can be safely removed.

I’ll leave the final word on performance to Matt Wilcox, who articulates this perfectly:

Performance is a browser level issue, it is not something for HTML/CSS authors to fix or work around. As long as we are authoring valid and sensible HTML and CSS, we should not need to resort to such ridiculous rules simply to enhance the speed at which a given page renders. Nor need we, these things are getting faster with every release. It’s the JS boys that need to worry most about optimising their code for performance. If you want to optimise your HTML/CSS - do it for best-practices, not for performance. “poor” performance will fix itself as new browsers come out, but shoddy hard-to-maintain and obscure code won’t. 7

So, what can we do?

Front-end developers have been quietly and successfully building large-scale web projects without the use of a CSS framework methodology for years. The fact that such projects have managed to avoid the use of OOCSS, BEM and others demonstrates that there are other effective ways of working. Here are a few of the recommendations from people with whom I’ve discussed authoring CSS, along with a few suggestions from my own experience.

“Use IDs, for the love of God”

Some CSS framework methodologists suggest that using IDs is a bad idea. The reasoning behind this is that IDs carry such a higher specificity that they risk making overriding rules elsewhere impossible without the (frowned-upon) !important modifier. I strongly disagree; as Angus Croll puts it: “The avoidance strategy bothers me because you can’t master a language until you know it inside out – and fear and evasion are the enemies of knowledge.” If you’re a professional web developer, you should understand how specificity works and how it will impact the selectors you write. What’s more, IDs are a valid part of a document’s structure and have very useful functional and semantic purposes.

“Write selectors that are as specific and descriptive as they need to be; no more, and no less”

If you’re writing CSS that targets an element that can appear anywhere in a document, use a selector that reflects that. If the CSS is only ever used in very specific circumstances, use a selector that reflects that instead. Above all, don’t make your selector so terse that its purpose is unrecognisable to somebody six months down the line.

“It’ll never be a reusable module (until it gets reused); refactor, rewrite, throw away all day long”

The fear of writing overly-specific CSS is, in effect, premature optimisation. Write the CSS you need to get the job done, then when it becomes apparent that it needs to be reused, refactor it into appropriate mixins, but not a minute before. Maybe that whole feature will be thrown away next week after a change in priorities? You wouldn’t want that to result in a bunch of redundant class name selectors that nobody understands the purpose of — or has any idea of the dangers of removing — six months later.

“Many files and a build process”

You’re a professional web developer. You don’t write in Notepad any more, so don’t restrict yourself from using the best tools to do your job. It doesn’t matter what framework or platform you’re developing on, there are CSS preprocessor tools available to you. There are also plenty of good examples of how to effectively re-use CSS across projects. Read up and see how it’s being done elsewhere; broaden your knowledge.

“Build static prototypes. Take screenshots of everything”

Websites and applications are typically formed from assemblies of common idioms; the form with labels and input boxes, the list of links within a <nav> element; the list of labels and values, and so forth. When approaching any given feature, start by building the markup that appropriately describes such structures as static HTML. This can serve not only as a test case to ensure that later code hasn’t broken it, but also to help give new team members visibility of what already exists to be taken advantage of. Taking screenshots of these pages or the application itself, either manually or through an automated test suite, can also be a useful way of detecting breakage through continuous integration testing.


Architecting good CSS in complex web applications is hard (that’s why good front-end developers are in such demand, after all), but we shouldn’t be making it harder both for newcomers and ourselves by imposing arbitrary ‘rules’ that ultimately come with unnecessary complexity and onerous maintenance costs attached.

By introspecting the purpose of the product you are building, you will be better placed to make decisions about how to structure every aspect of the product, not just how it is presented. The web is a semantic medium: let that be your guide.

It is the pervading law of all things organic and inorganic, of all things physical and metaphysical, of all things human and all things superhuman, of all true manifestations of the head, of the heart, of the soul, that the life is recognizable in its expression, that form ever follows function. This is the law.

Louis Sullivan 8

Thanks

In writing this article I was greatly assisted by the feedback of Mark Norman Francis, Brad Wright, Ross Bruniges, Jake Archibald and Patrick Griffiths.

References