How To Avoid Spaghetti CSS and Still Feel Fast and Productive

Ever feel like you are just slapping classes on HTML elements, throwing a bunch of properties and values in your CSS, and the next thing you know you have a 500-line monster of a CSS file?

I've been there.

First comes file monstrosity. Then, when CSS files grow too large, you split it up into multiple files, but really you are just kind of spreading the spaghetti around.

Then comes the specificity hell.

Classes overriding classes. Adding styles and screaming "why isn't this style changing!!". Finally yelling "screw it" and using !important 22 different times.

Why do we fall into the spaghetti CSS code trap?

Because we love CSS. We love seeing things come to life in the browser.

We love being able to quickly iterate and when things line up how we want, we feel a strong sense of accomplishment and want to move on to the next line of CSS, because deadlines, and "oh well I can come back and refactor that later if I need to."

But do we ever come back to refactor? Usually not. Why? Because we need to make quick changes and rather than untying a knot, we can just add another class to overide the existing class.

Thus, all-you-can-eat spaghetti!

So how, then, can we combine speedy CSS writing while also avoiding said spaghetti?

Let's take a 3-pronged approach.

  1. CSS (or Sass) file organization
  2. Semantic Class Structures
  3. Property and Value Ordering within a class

1. CSS (or Sass) file organization

Even if you haven't written Sass yet because you are still learning - this is the perfect time to start learning Sass through file organization.

You can write vanilla CSS in a Sass file and simply use an underscore in front of each Sass file name.

Our Sass file and folder structure will look something like this:

In the above example, we have divided our modules and global styles into folders, each containing file partials. Our compiler (Prepros is a super easy drag-and-drop compiler) will ignore these partials and only compile our main.scss into main.css.

Therefore, our main.scss will contain an @import statement for each partial in our two folders. It will look something like this:

/* main.scss */
@import 'globals/typography';
@import 'modules/widgets';
@import 'modules/postSummary';
@import 'modules/postSuggestions';

How do you decide on your Sass folder structure?

Great question!

Breaking things up into different files is for your benefit. Afterall, everything will eventually be compiled and minified down into a one or a few CSS files. Therefore, I find it easiest and most beneficial to break up files into modules and globals.

Global File Partials

If you are building a WordPress theme, for example, you can create a typography global partial (ex: _typography.scss), with basic typographic hierarchy.

This makes perfect sense, since articles and blog posts will be a staple content structure.

If you are building a web app, it may be more advantageous to have, say, an icons partial (ex: _icons.scss) in your globals folder.

Keep your global Sass files light and generic.

For _typography.scss in a WordPress theme, I may just declare font-family, line-height, font-size across breakpoints, and margin-bottom so things stack nicely.

I would avoid declaring things like special padding to accommodate icons next to a font, or special colors. These special cases can be part of a module.

For an _icons.scss in a web app build, I may just declare the file paths to the icons themselves. Nothing more.

These global declarations will be element-level styles, so be very judicious when creating them, or you can fall back into override hell.

So should you create a _button.scss, for example?

Well, if you know all your buttons are going to have a certain border-radius, then creating a _buttons.scss with those radiuses for the <button> element or .button class makes sense. Just make sure whatever styles you put into your global partials are styles you never, or very rarely, need to override.

Think of your global partials as a thin layer of your own custom CSS normalize.

Module File Partials

Modules are basic groupings of HTML element structures or combinations. There are no hard and fast rules here as to how many elements are in a module. Whatever makes most sense to you and your project structurally and semantically.

For example, let's say you have a new module you decided to title _postsNextPrevious.scss

Let's assume this component consists of a headline, thumbnail image, an arrow icon, and is wrapped in a block-style link.

If instead you decided to create _thumbnails.scss, _headlines.scss, and _block-link.scssfile partials, you would now be working across four different files, just so it can all be "reusable and modular".

Remember, the goal isn't to create a Bootstrap-like framework, the goal is to create maintainable project-specific Sass.

What really matters is being able to work efficiently, and communicate with others. Massive separation of concerns across Sass files will kill efficiency and communication.

If you can tell a manager about your progress on the timeline display widget, and talk to the backend dev about the data you need from the API for the timeline display widget, and be working in a HTML file titled timelineDisplay.html and a Sass file partial called _timelineDisplay.scss, then all your coding and business requirement worlds are in alignment.

What a joy! Work is fun again!

You can deliver things faster, optimize or refactor easier in the future, and ultimately build cooler stuff because your mental bandwidth isn't tangled up in file separation.

2. Semantic Class Structures

In general your Sass files should contain mostly classes, and use a consistent syntax structure to indicate what is a baseline class, and what classes are modifiers or states of the baseline class.

BEM is a syntax structure. It stands for Block-Element--Modifier.

Think of a block as a module in our file structure examples. Same concept, different name.

Then, think of an element as a single HTML structure within the module.

The modifier could be a state or variation of the overall module or an element within the module.

Therefore, in our above _postsNextPrevious.scss example, we may have the following classes:

/* _postsNextPrevious.scss */

/* module */
.postsNextPrevious {
    display: inline-block;
    width: 50%;
    background-repeat: none;
}
/* module modifiers */
.postsNextPrevious--previous {
    background-image: url(images/icon-left.svg);
}
.postsNextPrevious--next {
    background-image: url(images/icon-right.svg);
}
/* elements */
.postsNextPrevious-title {
    /* title styles here */
}
.postsNextPrevious-thumbnail {
    /* thumbnail styles here */
}

Our corresponding markup would look something like this:

<div class="container">
    <a href="" class="postsNextPrevious postsNextPrevious--previous">
        <img class="postsNextPrevious-thumbnail" src="" alt="">
        <h3 class="postsNextPrevious-title">Previous Headline Title</h3>
    </a>
    <a href="" class="postsNextPrevious postsNextPrevious--next">
        <img class="postsNextPrevious-thumbnail" src="" alt="">
        <h3 class="postsNextPrevious-title">Next Headline Title</h3>
    </a>
</div>

Here, perhaps .container is a simple global class with a display: block; width: 100%; we can use anywhere we need it.

When taking this approach, you may begin seeing yourself declaring certain value: properties repeatedly. It can be tempting to create "utility" classes you can sprinkle around in your HTML when you want to make some text "red" or position something to the "right".

In my experience, "utility" classes mostly lead to:

3. Property and Value Ordering within a class

Here, you can take whatever approach you wish, but I prefer to bucket properties into 4 main categories.

Therefore, one of my module classes may look like this:

/* _module.scss */
.module {
    /* display props */
    display: flex; 
    position: relative;
    
    /* Dimensional props */
    width: 25%; 
    padding: 1em;
    
    /* Typography props */
    font-style: italic;
    
    /* Decorative props */
    border: 2px solid #ccc; 
    color: #999;
}

The comments above are just for brevity, I don't actually comment out each property category when writing classes.

This is an approach of my own. Feel free to organize your properties and values how you wish within your classes, but organizing them will help with speed when writing your classes.

It will also give you a focus on what properties are common to a class, and which ones need to be separated out into modifier classes.

Certain decorative properties may need to be separated out into modifier classes, for example.

Conclusion

Having a consistent approach to file structure, class naming, and property organization makes CSS and Sass writing a faster, more enjoyable experience.

Also, remember your own mental bandwidth is more important than trying to keep code DRY, "reusable", and saving 50kb of internet bandwidth.