Mark Thomas Miller

How to write cleaner CSS

CSS is powerful. No other computer language has such a direct impact on the human psyche: it forms first impressions, informs user flow, and accelerates buying decisions. Entire companies are differentiated by their stylesheet1. And yet, the structure of our CSS is often an afterthought. How can we make our stylesheets more beautiful, powerful, and friendly to use?

I. Brevity

If your CSS is repetitive, create classes that consolidate the properties into one place. For example’s sake, let’s say you’re drawing three circles to the screen: one red, one blue, and one yellow. When the cursor hovers over a circle, you want it to become a pointer, and the circle should fade to green. A beginner might use the following HTML to create these circles:

<div class="redCircle"></div>
<div class="blueCircle"></div>
<div class="yellowCircle"></div>

And then, they’d type about 50 lines of CSS to achieve the desired effect:

.redCircle {
    display: inline-block;
    background: red;
    width: 100px;
    height: 100px;
    border-radius: 50%;
    cursor: pointer;
    -webkit-transition: background 250ms ease-in;
    -moz-transition: background 250ms ease-in;
    -o-transition: background 250ms ease-in;
    transition: background 250ms ease-in;
}

.redCircle:hover {
    background: green;
}

.blueCircle {
    display: inline-block;
    background: blue;
    width: 100px;
    height: 100px;
    border-radius: 50%;
    cursor: pointer;
    -webkit-transition: background 250ms ease-in;
    -moz-transition: background 250ms ease-in;
    -o-transition: background 250ms ease-in;
    transition: background 250ms ease-in;
}

.blueCircle:hover {
    background: green; 
}

.yellowCircle {
    display: inline-block;
    background: yellow;
    width: 100px;
    height: 100px;
    border-radius: 50%;
    cursor: pointer;
    -webkit-transition: background 250ms ease-in;
    -moz-transition: background 250ms ease-in;
    -o-transition: background 250ms ease-in;
    transition: background 250ms ease-in;
}

.yellowCircle:hover {
    background: green;
}

But this code is repetitive, lengthy, and tedious to change. For instance, if you wanted to add a purple circle, you’d need to create a new class called .purpleCircle and add 10 properties inside of it; if you wanted to change the transition property of the circles, you’d need to edit three different classes.

Fixing the code, however, is easy. Since we have multiple circles on the screen with the exact same properties, we can create a reusable class called circle and then custom classes for each color, such as red and blue.  We’d make a few small changes to the HTML:

<div class="red circle"></div>
<div class="blue circle"></div>
<div class="yellow circle"></div>

This lets us decrease the entirety of the CSS to:

.red { background: red; }

.blue { background: blue; }

.yellow { background: yellow; }

.circle {
    cursor: pointer;
    width: 100px;
    height: 100px;
    border-radius: 50%;
    -webkit-transition: background 250ms ease-in;
    -moz-transition: background 250ms ease-in;
    -o-transition: background 250ms ease-in;
    transition: background 250ms ease-in;
}

.circle:hover {
    background: green;
}

This format is also more maintainable: if you wanted to add a fourth circle, you’d just need to make a class for its color and reference the circle class in your HTML.

Not only will this CSS load faster, but it scales more easily, there is less room for error, and it will be easier for you to change in the future.

II. Structure

There are many ways to organize a stylesheet, and each have their pros and cons. I do a lot of work on simple or single-page websites, so my favorite method is to organize CSS in the same order as its elements appear in the HTML. I prefer this method because it’s navigable, understandable, and great for CSS’s top-to-bottom prioritization:

h1 {
    font-size: 1rem;
}

/* Takes precedence over the above rule. */
h1 {
    font-size: 2rem;
}

Basically, this means that if your HTML is structured like:

<header>...</header>
<div class="content">
    <div class="hero">...</div>
    <div class="testimonials">...</div>
    <div class="pricing">...</div>
</div>
<footer>...</footer>

Then you can structure your CSS in the following order:

  1. global elements, such as general typography settings
  2. the header and its child elements
  3. the content div and its child elements
  4. the footer and its child elements
  5. media queries for responsiveness

III. Readability

Creating the proper structure for your CSS will be difficult if you can barely read it. My favorite approach to this is to create multi-line titles for each section and usually expand each block of code:

/* Multiline titles are easy to find when scanning: */

/* ==================================== */
/* //////// GLOBAL TYPOGRAPHY ///////// */
/* ==================================== */
 
/* Elements with one property are (usually) not expanded. */
.oneProperty { display: block; }

/* Elements with two or more properties are expanded. */
.twoProperties {
    display: inline-block;
    margin: auto;
}

/* Multiple selectors stay on the same line... */
h1, h2, h3, h4, h5, h6 {
    font-family: "Proxima Nova";
    color: #222;
}

/* ...unless they're extremely lengthy. */
.thisIsAReallyLongPropertyOne .ohNoItKeepsGoing,
.thisIsAReallyLongPropertyTwo .whyIsThisSoLong,
.whoeverNamedTheseIsTerrible .fingersHaveNowFallenOff {
    line-height: 100%;
}

Of course, there will be times when you need to break one of these rules. I wouldn’t view this as the end of the world – just keep the code readable.

IV. Variables

If you’re not able to use a CSS preprocessor to create variables, you can still group together classes with similar properties. For instance, in the following example, you could change the font across all paragraphs and list items on your website by editing a single word (Avenir2).

/* Unique properties for paragraphs. */
p {
    font-size: 1rem;
    font-weight: 400;
}

/* Unique properties for list items. */
li {
    font-size: .9rem;
    font-weight: 300;
}

/* Properties that will always be shared by both. */
p, li {
    font-family: "Avenir";
    margin-bottom: 1rem;
}

V. Root EM

You may have seen me using rem as a sizing unit in these examples. rem stands for “root em”:

“An em is a CSS unit that measures the size of a font, from the top of a font’s cap height to the bottom of its lowest descender. Originally, the em was equal to the width of the capital letter M, which is where its name originated.”
– The Principles of Beautiful Web Design

I look at rem as a “quality of life” tool: it’s basically a variable that you can reference throughout your CSS in order to calculate values that place nicely with each other. rem is always the font-size of the html element, so you can set it up like so:

html {
    font-size: 20px;      /* rem */
}

h1 {
    font-size: 2rem;      /* becomes 40px */
} 

p {
    font-size: 1rem;      /* becomes 20px */
    margin-bottom: .8rem; /* becomes 16px */
}

Why does this matter? Well, you could change a single value – the font-size property of html – and every other element would scale accordingly. I’ve found rem to be a wonderful tool for testing font sizing and spacing.

As a related side note, do you notice how each element on this site is so evenly spaced? That’s because I repeatedly reference a single value (the rem) and calculate most of my font sizes and spacing from it3.


This is by no means an exhaustive list, but these are some good things to think about if you’re interested in writing better CSS.

Footnotes

  1. Compare the design of Blogger and Medium, which both contain the same writing-and-publishing functionality. Which one would you want to use?
  2. Although let’s be serious – there are very few circumstances where there’s a better choice than Avenir.
  3. I find that designs built off of a single value are often very pleasurable to look at. This adheres to the Gestalt principles of similarity and proximity, if you’re interested in learning more.