CSS Code Smells

Every week(ish) we publish the newsletter which contains the best links, tips, and tricks about web design and development. At the end, we typically write about something we’ve learned in the week. That might not be directly related to CSS or front-end development at all, but they’re a lot of fun to share. Here’s an example of one those segments from the newsletter where I ramble on about code quality and dive into what I think should be considered a code smell when it comes to the CSS language.

A lot of developers complain about CSS. The cascade! The weird property names! Vertical alignment! There are many strange things about the language, especially if you’re more familiar with a programming language like JavaScript or Ruby.

However, I think the real problem with the CSS language is that it’s simple but not easy. What I mean by that is that it doesn’t take much time to learn how to write CSS but it takes extraordinary effort to write “good” CSS. Within a week or two, you can probably memorize all the properties and values and make really beautiful designs in the browser without any plugins or dependencies and wow all you’re friends. But that’s not what I mean by “good CSS.”

In an effort to define what that is I’ve been thinking a lot lately about how we can identify what bad CSS is first. In other areas of programming, developers tend to talk of code smells when they describe bad code; hints in a program that identify that, hey, maybe this thing you’ve written isn’t a good idea. It could be something simple like a naming convention or a particularly fragile bit of code.

In a similar vein, below is my own list of code smells that I think will help us identify bad design and CSS. Note that these points are related to my experience in building large scale design systems in complex apps, so please take this all with a grain of salt.

Code smell #1: The fact you’re writing CSS in the first place

A large team will likely already have a collection of tools and systems in place to create things like buttons or styles to move elements around in a layout so the simple fact that you’re about to write CSS is probably a bad idea. If you’re just about to write custom CSS for a specific edge case then stop! You probably need to do one of the following:

Learn how the current system works and why it has the constraints it does and stick to those constraints

So the problem here isn’t necessarily the styles themselves but the concept of a ‘support page’ in the first place. When we write CSS we need to think in much larger abstractions — we need to think in templates or components instead of the specific content the user needs to see on a page. That way we can reuse something like a “card” over and over again on every page, including that one instance we need for the support page:

This is already a little better! (My next question would be what is a card, what content can a card have inside it, when is it not okay to use a card, etc etc. – these questions will likely challenge the design and keep you focused.)

Code smell #3: Styling HTML elements

In my experience, styling a HTML element (like a section or a paragraph tag) almost always means that we’re writing a hack. There’s only one appropriate time to style a HTML element directly like this:

section { display: block; }
figure { margin-bottom: 20px; }

And that is in the applications global so-called “reset styles”. Otherwise, we’re making our codebase fractured and harder to debug because we have no idea whether or not those styles are hacks for a specific purpose or whether they define the defaults for that HTML element.

Code smell #4: Indenting code

Indenting Sass code so that child components sit within a parent element is almost always a code smell and a sure sign that this design needs to be refactored. Here’s one example:

.card {
display: flex;
.header {
font-size: 21px;
}
}

In this example are we saying that you can only use a .header class inside a .card? Or are we overriding another block of CSS somewhere else deep within our codebase? The fact that we even have to ask questions like this shows the biggest problem here: we have now sown doubt into the codebase. To really understand how this code works I have to have knowledge of other bits of code. And if I have to ask questions about why this code exists or how it works then it is probably either too complicated or unmaintainable for the future.

This leads to the fifth code smell…

Code smell #5: Overriding CSS

In an ideal world we have a reset CSS file that styles all our default elements and then we have separate individual CSS files for every button, form input and component in our application. Our code should be, at most, overridden by the cascade once. First, this makes our overall code more predictable and second, makes our component code (like button.scss) super readable. We now know that if we need to fix something we can open up a single file and those changes are replicated throughout the application in one fell swoop. When it comes to CSS, predictability is everything.

In that same CSS Utopia, we would then perhaps make it impossible to override certain class names with something like CSS Modules. That way we can’t make mistakes by accident.

Code smell #6: CSS files with more than 50 lines of code in them

The more CSS you write the more complicated and fragile the codebase becomes. So whenever I get to around ~50 lines of CSS I tend to rethink what I’m designing by asking myself a couple of questions. Starting and ending with: “is this a single component, or can we break it up into separate parts that work independently from one another?”

That’s a difficult and time-consuming process to be practicing endlessly but it leads to a solid codebase and it trains you to write really good CSS.

Wrapping up

I suppose I now have another question, but this time for you: what do you see as a code smell in CSS? What is bad CSS? What is really good CSS? Make sure to add a comment below!