Go From Cee-Ess-Ess To Cee-Ess-YES!
I hate CSS.
Well, I hated CSS. While learning to write code, CSS seemed like an inoperable black box. The twin tasks of deciding how something should look, and then making it look that way seemed just impossible. Which resulted, very naturally, in a deep-seated hatred of the entire art.
It turns out, though, that a lot of that distaste sprung from not having a process in place to structure and write good CSS rules. As I’ve moved between building applications from scratch, and jumping into already established codebases, I started to compile a little set of, not rules per se, but rather things-that-work, which is, y’know, like having rules but good for us anti-authoritarian types. And one day I got into work and realized that I was really stoked for the tasks I had on my to-do list that day… and they were all CSS.
Shock. Awe. Mild-to-moderate terror. How in the name of hand-built components did that happen?
It turns out that CSS is ultimately like any other kind of programming. It is first and foremost a task of problem decomposition, and once you have a tidy set of discrete to-do’s, it’s just as satisfying and delightful as any other kind of programming (fight me, internet).
What follows is a collection of standards and practices for writing effective CSS. This is far from the only way to do it — if you only take one thing away from this post, it will hopefully be that it’s more important to have a system than it is to have this system, but if you don’t have any system, this one is not a bad place to start. With just four, fairly straightforward guidelines, you too can hate CSS less than you do now.
1. Classes Are For Styles.
Tags are for semantics, Ids are for references.
Every CSS rule I write is tied to a class, and ideally, only to a class. Right from the start, it means that when I am looking at my document and trying to understand why it looks the way it does, I know exactly where to start tracking down the applicable CSS rules, and it helps minimize conflicts with unpredictable outcomes and spaghetti code.
This has been key to keeping my CSS clean and well-organized, and it has a big payoff for the usability of any application where you apply it as a principle. That payoff is a good old coding favorite: the separation of concerns. In this case, the separation of visual presentation from semantic meaning.
If you’re writing your HTML (or, let’s be real, your JSX, because who is even writing HTML these days) properly, you probably don’t have a ton of
<div />s in your codebase — why use a plain old
<div> when you can have an
<article> or a
<form> or a
<nav> or a
<main>? You’re not forgetting to add proper landmarks to your page views, right?) And it might be tempting to style on those elements, especially if you think, “well, hey, we probably want all of our menus to be consistent, don’t we?”
But writing good semantic HTML means that you choose your elements based on their function in your page, and the meaning of their content, not based on how you want them to look. An
<h1> is the single most important bit of context on your view, not “text you want to be big and in a stylized signage typeface.” And while a lot of times, these two concerns will flow together, occasionally they will diverge, and if you’ve written your styles on that
<h1> , at that point, you have two options:
- You can write some extra CSS rules so that you have
<h1 class="special-not-big-h1">( or worse,
<h1 id="special-not-big-h1">which is reallygoing to cause some heartache down the line), adding to the complexity and lowering the readability of your codebase. (Which, if you do it once, won’t be such a problem, but we all know how quickly these little hacks and special cases start to add up, don’t we?)
- You can decide not to use an
<h1>for that view’s important content, and thus leave folks using screen readers or other assistive technologies in the dark about the hierarchy of your page. And that would really suck.
Whereas, if you haven’t made your
<h1>‘s do double-duty as semantic elements and stylers, all you have to do is not add that particular class to the special-case element. Isn’t that easier? And cleaner?
A big part of the point here is break the mental association between “semantic element” and “visual style.” If what you want is “big text”, put that in your css, don’t reach for an element that doesn’t necessarily belong there.
2. BEM will Block the Modification of your Elements from being a Certifiable Trash Fire (The CTF Paradigm of CSS)
BEM stands for “Block, Element, Modifier,” and is a set of naming conventions that help keep your CSS organized. There are quite a few systems of naming conventions out there, and there’s no single one that is the best option for all circumstances and projects — as with a CSS system generally, it’s more important to have one than to have any particular one. But BEM is pretty handy, and I think it’s a great place to start for a wide range of applications (in part because it’s well known, so it won’t be hard to explain to new devs joining your project in the future), so let’s talk about what it is, and why it’s great.
BEM separates our CSS rules into three categories, which give a logic to how visual style is applied to your html. Instead of taking each visual requirement as a standalone task, BEM encourages you to think of entire structure of your view, and to break down your styles accordingly. Wherever you store your CSS, all of your rules should be grouped into these three sections (organized either hierarchically or alphabetically, whatever makes sense to your team).
- Blocks: Blocks are standalone entities in your code, which contain meaning entirely on their own. Good examples are headers, inputs, menus, checkboxes, and so on. A block is something that essentially, contains all of its own rules without any reliance on or reference to parent elements. Classes for blocks are defined without underscores or hyphens, and using camel-case to separate different words, like so:
.orderedList. (Frequently, you’ll see BEM where a single hyphen is used to separate words in blocks, since that’s a more common CSS convention outside of BEM, but I think personally that it causes visual confusion with the naming of elements and modifiers, and adds to the likelihood of typos ).
- Elements: Elements are component pieces of Blocks, and their names should be semantically tied to their parent Block. They do not work as standalone pieces — we’re talking about things like nav links, list items, or input captions. Elements are named with their parent block followed by their own descriptive name, separated by two underscores:
- Modifiers: Modifiers are flags that can go on Blocks or Elements, to change appearance or behavior of the particular item (often, these are your special cases, where the color, size or placement has to be treated differently). Modifiers are named by appending two hyphens and then a descriptive name after the Block or Element:
.list--collapsed, and so on. Modifiers are also where I’ll group style rules for pseudo-classes, since that’s essentially what they are.
It was hard at first for me to really see the utility of this system, until I took an existing project and migrated it over to BEM conventions. The next feature/view set I build out went so much more smoothly than the ones before it, I was completely floored.
3. Accessibility Is A Great Design Guide
Sometimes, and I know that this is hard to believe but bear with me, you write code without high-fidelity, full-featured wireframes. Sometimes your graphic designer sucks, or doesn’t exist, or is doing twelve million other projects and just isn’t a part of your daily workflow. And more and more often, front-end and full-stack web developers are expected, at least in part, to do the work of a designer.
So, if you’re trying to develop your aesthetic, and you just don’t know where to start, start by thinking about what, visually, will make your application usable. And naturally, when I say usable, I mean usable to as many people as possible, not just the currently able-bodied ones (that’s what we all mean, RIGHT EVERYBODY?).
The nice thing about developing accessible applications is that, for the most part, they make things better for all of your users, regardless of how they consume your content. If you’re not sure how something should look, start thinking about how to style it in a way that works for as many people as possible. Make sure your line-heights are at least 1.5em, your font-sizes are big enough, your color contrast is 4.5:1 or higher, and that your line-lengths are bounded to keep text at 80 characters per line or less. And definitely don’t use color alone to communicate important information to your user (especially, especially not red vs. green).
True accessibility is outside the scope of this essay, but I encourage anyone who writes code, especially code that touches a UI, to learn more about it, and to be an active advocate for usable, accessible design on their team. Deque has a phenomenal online curriculum available to anyone with a web connection (and with a full scholarship to anyone with a disability), and the World Wide Web Consortium (W3C), has a checklist on how to meet different levels of accessibility standards, for folks who don’t have the resources for Deque, or who just need to make a quick reference check. Even when you do have a designer working with you every step of the way, they may not know any more about accessible design than you do, and the voice of someone who knows what’s going on under the hood can be an important one.
4. The Only Good Shame Is Shame.css
I wish I were smart enough to have coined the phrase and concept of Shame.css. Alas, I did not, so I have to start by crediting and singing the praises of Harry Roberts, a literal CSS Wizard, who explains what Shame.css is and why it is so handy here and here.
But let me talk to you a little bit about how and why I use it in my projects or those I work on. I’d be lying (and unconvincingly, too), if I told you that I followed all the principles I’ve laid out here with perfect consistently 100% of the time. Sometimes, you just need to tinker with what a heading looks like a million times, and you don’t want to go in and add a new class on all your headings. Sometimes you need something fixed now-now-NOW and clean code has to take a back seat to functionality. Sometimes, it’s a Friday afternoon and you’re just too exhausted to think straight. Whatever the reason, sometimes you do it the easy way instead of the right way.
These things happen, and if we pretend that they don’t, we lose the opportunity to contain the destruction that they cause.
Enter, Shame.css: when you write some CSS that is hack-y and gross, own it. Put it in its own special CSS file (or just as a fourth section along with
/* blocks */,
/* elements */, and
/* modifiers */ — have a
/* FIX THIS SHIT ASAP */ portion too). Now your code is working, but you’ve taken steps towards ensuring that future-you doesn’t wind up with a snarled mess of bad styles to deal with in the future.
The key with Shame.css (just for the record, in my own code, I usually call it “to-fix.css” or “hacks.css”, just to be less negative and blamey about it), is that now, part of your workflow has to be circling back to that file, and actually straightening out whatever CSS-sins you’ve committed in the name of having a quiet life. Ideally, you should get right to it the next time you have a down moment, but since those can be hard to come by organically, I like to officially schedule time no less frequently than once a week for it. Monday mornings can actually be a great time for this — it’s a really nice way to ease in to the week with some not-too-hard, virtuous-feeling wins that don’t require a ton of decision making.
Whenever you do it, just make sure you do it, otherwise, you’re not reducing complexity, you’re just ignoring your own system.
As always, I want to hear all about where you think I’m wrong, what you think I’ve missed, and how you totally would do it better. How did you start to hate CSS less? How did you start writing it better?
Full Stack Developer. Currently telling stories with data @ Locus Analytics. Former Teaching Fellow @ Grace Hopper. Bug me to blog about D3. pronouns: she/they
Bursts of code to power through your day. Web Development articles, tutorials, and news.