Made Tech Blog

Rules for Stylesheet Modularity

Keeping applications organised takes a lot of work. Furious bursts of development where deadlines are tight can lead to poorer design decisions. The frontend in particular for me is harder to get right when the pressure is on. I’m writing this post in order to clarify my hard and fast rules for writing modular stylesheets in a rush.

My flavour of stylesheet modularity is in many ways a BEM implementation though I’d certainly say I am no expert in BEM. Also it’s worth noting these rules are applicable to all stylesheets whether CSS, SCSS, LESS, or whatever.

Without further ado, here are my rules:

  • One BEM module per file
  • Never reference one BEM Block from another
  • Never reference one BEM Element from another
  • Move repeating Element classes into their own Blocks
  • Keep your modules less than 100 lines long
  • Never name more than one class on an element
  • Do not style elements without classes
  • Compose rather than inherit

One BEM module per file

Example of doing it wrong

.primary_navigation {}
.secondary_navigation {}

Why

It is easier to find modules if you name your stylesheet files after their BEM Block class name. Not only that but longer files are a pain.

Notable exceptions

None.

Keep your BEM modules less than 100 lines long

Example of doing it wrong

I think you can understand this one without an example.

Why

If your BEM module is getting lengthy then it’s often a sign it’s doing too much. Breaking down large modules makes your code easier to reason about. It also makes finding styles much easier!

Notable exceptions

If it’s just over 100 lines don’t go crazy on yourself. If you’re hitting 150-200 then you ought to start breaking your module down.

Never reference one BEM Block from another

Example of doing it wrong

.primary_navigation {
  .secondary_navigation {}
}

Why

It is easier to reason about where styles come from if all responsible styles are defined in one file. You can often tie yourself in knots when BEM modules become concerned with one another. This also breaks the first rule.

Notable exceptions

None.

Never reference one BEM Element from another Block or Element

Example of doing it wrong

.primary_navigation {
  .primary_navigation__link {}
}
.primary_navigation__link {
  .primary_navigation__icon {}
}

Why

Again it’s easier to reason about styles when you do not nest style definitions inside one another. It simplifies specificity problems.

Notable exceptions

When the BEM Block is a modified Block.

.primary_navigation__link {}
.primary_navigation--mobile {
  .primary_navigation__link {}
}

This is okay since the modified Block should affect the styles of the Element.

Move repeating Element classes into their own Blocks

Example of doing it wrong

.primary_navigation__link {}
.primary_navigation__link_icon {}
.primary_navigation__link_title {}
.primary_navigation__link_description {}

Why

When you see commonly repeated words in Element classes this often is a sign that you should break them out into their own module.

In the example of doing it wrong above, .primary_navigation__link could become .primary_navigation_link.

.primary_navigation_link {}
.primary_navigation_link__icon {}
.primary_navigation_link__title {}
.primary_navigation_link__description {}

Notable exceptions

If it’s one or two classes that are showing signs of being a new BEM Block I might leave it until there are three.

Never name more than one class on an element

Example of doing it wrong



Why

When you apply more than one class to an HTML element both those classes will now be fighting for control over that HTML element. If you instead use two different HTML elements you will save yourself a lot of headaches.

A corrected version:




Notable exceptions

One exception to this rule would be Modifier classes applied by JS. Imagine you have an expanded version of your primary navigation, .primary_navigation–expanded that gets enabled and disabled by JS. Instead of removing the existing class you might instead just add the class to the element. This is okay since a Modifier extends from the original class.

Do not style elements without classes

Example of doing it wrong

.header {
  a {
    color: green;
    &:hover {
      color: blue;
    }
  }
}

.primary_navigation {
  a {
    color: red;
  }
}

Why

Specificity is the problem yet again. If your BEM Block or Element finds itself inside another BEM module and both style an element you will sometimes get confusing results.

In the example above, when hovering over a link in .primary_navigation the link would in fact go blue instead of staying red like you may expect.

Instead of styling HTML elements you should add a class to the element and style that, like so:

.header__link {
  color: green;
  &:hover {
    color: blue;
  }
}

.primary_navigation__link {
  color: red;
}

Notable exceptions

When styling WYSIWIG generated content that does not have any BEM classes it is acceptable to style elements.

When I’m feeling lazy I do break this rule and it could perhaps be my most broken rule.

Compose rather than inherit

Example of doing it wrong

.primary_navigation {
  @extend %navigation;
}

.secondary_navigation {
  @extend %navigation;
}
.primary_navigation {}

.secondary_navigation {
  @extend .primary_navigation;
}

Why

Inheriting using @extend in SCSS couples your BEM modules too close together. When this rule is broken alone it’s not so much a problem. However if you break my “Do not style elements without classes” or “Never reference one BEM Element from another Block or Element” rule as well then you can get some funky results. This is especially true if the class or placeholder you extended comes later in the alphabet than the class extending it. I’ll leave the rest of this why to the reader at home.

Notable exceptions

None.

Summary

So there you have ’em. Learn these rules and practice them regularly and when the heat turns up at work you’ll hopefully not have a big ball of styles to contend with a week or two later.

About the Author

Avatar for Luke Morton

Luke Morton

Chief Technology Officer at Made Tech