T S Vallender

Maintainable, context-driven CSS

The problem with maintaining CSS


CSS has come a long way in recent years, taking on many features first explored through extension languages such as Sass and Less. One big problem remains however; maintainability. Most projects I’ve seen that don’t use a framework (such as Bootstrap) struggle hugely with this; it’s far too easy to end up with a morass of stylesheets which interact with each other in unintended and unforeseeable ways, and quickly becomes easier to add CSS to the existing swamp than try and figure how to reuse what already exists.

In this post I want to explore my own approach to writing and maintaining CSS, which I believe is a great way to do so, and avoids adding in any dependencies on toolkits or frameworks. I claim no level of originality in this approach—it feels, to me, like the approach originally intended by CSS—but am presenting it here as I’ve yet to see anyone else practice this approach in any serious way. As I’m writing about the approach, I thought I should coin a term for it, so I’m dubbing it Context-Driven CSS. You’ll see why a little later.

Toolkits


As a quick aside, you might wonder why I don’t simply recommend using a toolkit such as Bootstrap, as earlier I hinted they’re a way to avoid maintainability issues. I’m not a huge fan of these sorts of toolkits—I don’t like the HTML they end up fostering nor the homogeneity of styling they produce. Even I, however, think they can be a fantastic choice for private applications where originality of design is not a selling point. They can help non-designers quickly build sites that look decent and consistent.

Context-driven CSS


It’s generally regarded as good practice to keep your HTML as semantically correct as possible. This means using the correct tags in the correct places to describe what your data is (e.g. nav for navigation, main for your main content, aside for, well, asides, you get the idea). Most modern CSS approaches, however, end up drowning those tags in meaningless divs and/or endless classes.

With our approach, however, we use these things only minimally, opting to style the tags themselves based on the context they’re in (e.g. is this a list in the navigation? Or in the sidemenu? Or simply in the article?).

One immediate way in which this approach lends itself to maintainability is removing the need for the author of new markup to know and use the correct classes they need to apply. Simply use semantic HTML (which you should be doing anyway), and the correct styles should be applied. When it comes to adding CSS, it also becomes more obvious where to add it—simply think what context the new style will be needed in. Is this useful in sidebars? Articles? Style the appropriate HTML tags in the correct context, and all those things will be styled correctly in the future.

I mentioned nesting there in passing—and that’s generally the best way to organise this code now that vanilla CSS supports it.

Here’s a very simple example styling an unordered list as a horizontal menu or standard list in different contexts:
nav {
  ul {
    text-align: center;
    li {
      display: inline;
      list-style-type: none;
    }
  }
}

main {
  li {
    list-style-type: square;
  }
}

(Note the above syntax is yet to be supported by all browsers—for now, nested classes may need to be prefixed by & or >).

Take a look at some of the CSS for this site for a slightly larger example (I was particularly pleased with the styling of forms into columns using this approach).

Limitations


Now, I do realise this doesn’t cover every eventuality. I am by no means claiming you’ll never need a class or div again. There will of course be places where the same tags need to be styled in distinct ways. There’s nothing wrong with reaching for a class in those circumstances, though I would suggest you take a second longer to consider whether you really can’t style based on context—sometimes it’s not immediately apparent. Also, if you do need a class, sometimes it makes more sense to add it to one of the element’s parents, thus creating a context for this element and any other children which might need to be styled differently here.

It’s also worth noting I’ve not had the opportunity to use this approach in anger on a large application. I believe it would work well (though it does require some discipline from the developers), but I don’t have the evidence to back that up.

Other tips


Custom properties (or variables, as everyone else in the world calls them), massively help maintainability. At the top of your first stylesheet define the colours, font sizes etc. your application uses and limit yourself to those throughout. This will keep your design consistent and make future changes much simpler.

I don’t have any grand ideas about organising the stylesheets themselves. I try and keep everything applicable to the entire application in a single file and start by defining global styles, then those nested under top-level elements such as main and header. Styles limited to particular parts of the application can be kept in well-named, separate stylesheets (and optionally only be included on those pages). It’s far from bulletproof however. I’m not sure the design of CSS as a language allows for a real solution to this problem.

My original inspiration for writing this post was the chapter on CSS in the brilliant book Sustainable Web Development by David Bryant Copeland. It includes some other good ideas to increase the maintainability of CSS (not to mention the rest of your application if you’re using Rails) and I highly recommend it.

Updated at 2023-10-16 08:01 | 2023-10-14 20:02