SUCSS: A conceptual framework for Scrappable, Unproblematic CSS

Introduction

It’s easy to dismiss front-end development as something that just happens if you put a developer in a room for four days with a laptop, some PNGs, and a standing desk. Truth is, shoddy CSS won’t sink the business and your code doesn’t need to be anywhere close to perfect. Your current tech problem is, in almost-certain fact, “not a tech problem.”

Someday though, there’ll come a moment when the original developer has left, two new developers are starting, and the business needs to pivot. Or maybe it’s growing. The new designs are ready. Something is going to happen, and it’s either going to amount to a new coat of paint or scorched-earth death and rebirth. This is the moment when the original code is exposed in all of its beautiful and terrible glory.

I’m not going to write about great CSS. There’s a lot of that. I’m sure Medium’s CSS is actually pretty f***ing good. Nicolas Gallagher has some illuminating thoughts on semantics. You should read about the difference between the SMACSS and BEM methodologies.

No, today I’m going to back up a couple steps and focus on conditioning a big red alarm bell to go off in your head when you’re wading into dangerous territory. Naming conventions have no bearing on this discussion, but to be clear, I’ll write the examples in SASS, will opt for SMACSS-like notation with BEM-like minimalism, and I’ll call my approach SUCSS: Scrappable, Unproblematic CSS.

The problem: Unscrappable, Problematic CSS

Early on, I got backed into an interesting corner. I inherited a project (as if I couldn’t list five good examples of problems I caused). My task was to bring it to launch. There was a single 2269-line SASS stylesheet nested as many as seven levels deep. An actual rule:

1
#content.user-creations .right .user-stats .detail ul li.last label

I don’t need to tell you the front-end situation wasn’t great, but the problem wasn’t that I couldn’t add CSS rules. Adding rules is pretty much always easy. The problem was that I couldn’t remove rules. They were unscrappable. Pretty soon the CSS was 3000 lines. Then 3500 lines. I was pretty sure large swaths of the stylesheet no longer applied. But not totally sure. I split it into multiple files, but that only spread the confusion across multiple files. The CSS ceased to function as a system of rules because it was littered with too many unremovable gotchas.

The above rule is an example of tight coupling. It’s effectively unmaintainable. Is the label in question simultaneously and meaningfully connected to the list, the stats, the right column and the user creations page? Nesting SASS is easy, but at this depth the structure you’ve given it almost certainly does not reflect any meaning.

The solution, a concept underlying all CSS methodologies, is loose coupling. The details vary, but the question you need to ask is the same:

What happens if I move this piece of the view?

The desired answer, more often than not, is that it should look nearly the same. The corollary, I’ll go as far as to say, is that the worst thing you can do for your CSS is to mirror the DOM structure. Consider the HTML:

1
2
3
4
5
6
7
<div class="new-post blog row active">
  <div class="left-column col-md-6">
    <form>
      <input type="submit" class="btn btn-primary">
    </form>
  </div>
</div>

and corresponding SASS:

1
2
3
4
5
6
7
8
9
10
11
.new-post.row {
  &.active {
    .left-column {
      form {
        .btn {
          ...
        }
      }
    }
  }
}

There’s a laundry list of problems. If you move the .btn into the right column, you have to rework your CSS. A plain .btn styled anywhere else on the site will affect this button. Same for unrelated blog styles potentially affecting newPost. (Einstein called quantum entanglement “spooky action at a distance.” I call unexpected interaction with distant code “shitty action at a distance.”)

The harsh reality is that once things reach a certain level of complexity—not even very much—it’ll almost certainly become more efficient to leave the existing CSS in tact (just in case!) and write new rules in parallel. Your CSS becomes a wasteland of the overspecified concealing a minefield of the overgeneralized.

A solution: Scrappable, Unproblematic CSS

CSS has a way of getting out of control. Legacy styles become undeletable because who knows? I propose the rule of thumb,

If it’s not obvious whether a rule can be removed, your selectors are probably too general and your nesting too deep.

I want to emphasize that my CSS is not (f***ing) great, but here’s a suggested reworking of the above example. You may try rewriting the classes like this:

1
2
3
4
5
6
7
<div class="newPost newPost--blog is-active">
  <div class="newPost-leftCol">
    <form class="newPost-form">
      <input type="submit" class="newPost-submit">
    </form>
  </div>
</div>

If you’re using SASS, you could write your styles like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.newPost {
  @extend .row;
  ...
}

.newPost-leftCol {
  @extend .col-md-6;
  ...
}

.newPost-form {
  ...
}

.newPost-submit {
  @extend .btn;
  @extend .btn-primary;
}

.newPost.is-active {
  /* An active post. Don't style .is-* except with the thing it modifies! */
}

.newPost--blog {
  /* "--blog" signals we're modifying the look of the new post because
   * it's a new *blog* post */
}

There’s a lot going on here and BEM describes it more clearly and precisely than I’m able, so I’ll only ask you to notice three things:

  1. I haven’t used SASS’s nested braces. At all. Don’t. Selectors with more than two or maybe three levels almost certainly don’t need the meaning you’re giving them. And for two or three-level selectors, instead of nesting braces, write each rule separately, one per line. This will force you to think about what you mean.

  2. I used the moduleName-childName convention. Choose your own dash convention, but when you do, call things on the page modules. Give modules children. And choose names uniquely and consistently so they will not interact in unexpected ways.

  3. I’ve used SASS’s @extend to avoid littering my CSS with presentational classes. Whether to use col-md-6 or btn btn-primary btn-lg everywhere is a contentious issue. I prefer without, but as a matter of dogma I take no stance; I only want to point out @extend is a very useful thing you can do.

Things get subtle. Ambiguous. These are merely strong suggestions. There are countless details to work out. But if we’re grappling with these questions, we’re light years ahead of where we started.

A slightly more nuanced example

Applying these guidlines is never totally straightforward. Consider this simple but slightly more illuminating case:

1
2
3
4
5
6
7
<div class="userProfile">
  <div class="avatar">
    <div class="avatar-wrapper">
      <img src="...">
    </div>
  </div>
</div>

Perfectly lovely CSS is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.avatar {
  /* What does a generic avatar look like? */
}

.avatar-wrapper {
  ...
}

.avatar img {
  /* (Note this rule is *not* `.avatar .avatar-wrapper img`) */
}

.userProfile {
  ...
}

.userProfile .avatar {
  /* This is the interface between the two modules. The only sort of thing
   * here should be outward-facing properties that affect how userProfile
    * interacts with its surroundings. Things like: */
  display: inline-block;
  margin-top: 20px;
}

I don’t love this since the avatar shouldn’t care that it’s in a profile. But these things do happen sometimes. Perhaps we could write instead:

1
2
3
4
5
6
7
<div class="userProfile">
  <div class="userProfile-avatar">
    <div class="avatar-wrapper">
      <img src="...">
    </div>
  </div>
</div>

In this case the CSS for the avatar would be slightly different. Omitting the repeated parts, we’d write:

1
2
3
.userProfile-avatar {
  @extend .avatar;
}

I think they’re both lovely and the best choice depends on how you’ve set up your partials. Again, if this is the discussion we’re having, we’re doing well.

Conclusion

If you’re somehow here and even more miraculously still with me, you should definitely read the sources I’ve cited. They’re good. Real good. Way better than my half-baked blog post. BEM is straightforward. SMACSS is a little long. Still good though.

At its core, this isn’t even about CSS. It’s about expressing technical meaning simply and effectively so that you can create. So that you can collaborate. So that you can free your mind to worry about real meaning. Like creating things to earn money. Creating things to help people. Creating things so that you can feel good about your work and live your life. Take a walk. Climb a mountain. Fall in love. So that when someone asks you about your front-end technique, you can look them straight in the eye and reply with firm conviction and a pure heart, “Well it’s not perfect, but I do my best. My CSS? It’s SUCSS.”

Thoughts? Feedback? I don’t claim to be right. I’m only trying to be better. Let me know below if anything resonated or fell flat. Or if you have better ideas!