DEV Community

loading...

CSS Architecture (BEM, OOCSS, SMACSS & ACSS) and why we need it.

ianholden profile image Ian Holden Originally published at ianholden.co.uk ・8 min read

Have you ever worked on a large project where there is a clear structure in place for the JavaScript source files, but when you need to make a change to the CSS, you are greeted with a mess of unstructured CSS?

You know what CSS attribute you need to add or amend but in the code, you are cautious because there are CSS attributes being overridden in multiple places and in some cases, there are !important flags being used. You are scared to make a change to the code in fear of breaking some existing styles. So naturally, you create a new rule which has high specificity and just adds to the mess that you were greeted with in the first place.

This is a problem that a lot of projects suffer from.

Understanding how to organise CSS in your project's codebase can be invaluable when your project begins to scale or when new frontend engineers join the team. I would like to walk you through different ideas that can help you create stronger CSS architecture.

Why do we need strong CSS Architecture?

The foundations of a solid CSS architecture can uphold the project as it begins to scale and grow. If these foundations have not been constructed with any thought or planning, it is very likely that in the future, your project could metaphorically fall over.

What I mean by this metaphor is; although CSS will not likely break your system like a JavaScript error might, it can create different issues that make working on the project very difficult. If you have a team of engineers who are not confident in adding or changing CSS in the project, they are more likely to introduce bugs. These bugs will find their way to your end-users and the project's success may be affected because of this.

It is essential to begin a project with a solid CSS architecture in place because it is much easier to write bad CSS than it is to remove it.

My impression is that CSS is not as widely respected in the front-end space as other disciplines are. Why do we take so much time in learning about the latest JavaScript frameworks but also insist on getting by with a loose understanding of CSS? HTML and CSS are the building blocks of the web, yet we settle for an average understanding.

This is something that I think we have all been guilty of! It is something that I would like to change.

What CSS architectures can we use?

There were a number of CSS architectures that stood out when I started researching this topic. They were:

  • BEM (Block Element Modifier)
  • OOCSS (Object-Oriented CSS)
  • SMACSS (Scalable and Modular Architecture for CSS)
  • ACSS (Atomic CSS)

I do not want to describe these in too much detail as there are some excellent resources available online already. Instead, you can find a brief description of each one below and a link to more information about each methodology.

What is BEM (Block Element Modifier)?

BEM stands for Block Element Modifier and is a way of architecting CSS to encourage the use of a consistent naming convention when creating your style classes.

The naming convention that is recommended by BEM is block-name__element-name--modifier-name where:

  • block-name describes the block that it should style. For example: photo.
  • __element-name describes the element within that block that should be styled. For example: photo__image or photo__caption.
  • --modifier-name describes the modifier of the block or element that is being styled. For example: photo--large or photo__image--black-and-white.

For more information on BEM, i would recommend reading BEM 101.

What is OOCSS (Object-Oriented CSS)?

OOCSS stands for Object-Oriented CSS and is a way of architecting CSS to encourage the use of abstraction to separate structural and visual styling as well as to remove duplication from your CSS.

OOCSS would encourage you to change the following example from something like this...

.button {
    padding: 10px 16px;
    color: blue;
}
.h1 {
    font-size: 2em;
    color: blue;
}
Enter fullscreen mode Exit fullscreen mode

...to something like this...

.button {
    padding: 10px 16px;
}
.h1 {
    font-size: 2em;
}
.primary {
    color: blue;
}
Enter fullscreen mode Exit fullscreen mode

With our new abstraction, we can assign the 'primary' class to both <button> and <h1> elements and it will be given a colour of 'blue'. This helps us keep our stylesheets nice and DRY (Don't Repeat Yourself).

For a more detailed guide on OOCSS, I would recommend reading An Introduction To Object Oriented CSS (OOCSS).

What is SMACSS (Scalable and Modular Architecture for CSS)?

SMACSS stands for Scalable and Modular Architecture for CSS. It requires styles to be built under five different categories: base, layout, module, state and theme.

  • Base styles should hold your default CSS styles. These styles will be used on all elements of the website/app.
  • Layout styles should hold the styles for elements that are designed to separate the page into its structural components. Styles for elements like header, footer and grids should be defined here.
  • Module styles should hold the styles for reusable elements across the website/app.
  • State styles should define the various states of different elements across your website/app. Styles such as 'is-active', 'is-disabled' and 'is-success' should be found here.
  • Theme styles should dictate how your elements look. They should go beyond the Base styles and start injecting your project's branding and style into the website/app.

For a comprehensive look at SMACSS, read the online guide from its' creator at SMACSS.com.

What is ACSS (Atomic CSS)?

ACSS stands for Atomic CSS and focuses on creating many small CSS utility classes for you to use in your HTML. It is similar to OOCSS in that they both recommend separating duplicate property-value pairs into their own rules. ACSS could be seen as a more extreme version of OOCSS though, as it encourages you to create styles at the smallest possible level.

Here is an example of some style rules that you might find in a project that uses Atomic CSS:

/* Colours */
.color-primary { color: blue; }
.color-secondary { color: purple; }

/* Margins */
.mt-1 { margin-top: 5px; }
.mt-2 { margin-top: 10px; }
.mb-1 { margin-bottom: 5px; }
.mb-2 { margin-bottom: 10px; }
Enter fullscreen mode Exit fullscreen mode

ACSS is also different to OOCSS in that it recommends the use of CSS properties in the naming convention of CSS selectors. For example, in OOCSS, you might target a selector named .primary to change the colour of your elements to a nice shade of blue. In ACSS, you might target a selector named .color-primary.

To find out more about ACSS, I would recommend reading Let’s Define Exactly What Atomic CSS is.

Which CSS architecture should we choose?

The short answer — it doesn't matter.

There are positives and negatives to each different architecture described above. Different people will have different views over which they prefer.

The most important thing is to ensure that your team have agreed on a consistent format to use before you begin writing CSS. This format can be one of the architectures mentioned above, a blend of different architectures or something that you come up with yourself.

The engineering team that work on the project should understand this format so that any new CSS that is written, adheres to the style that you have chosen to follow. Preferably, the architecture should be explained in written documentation. New engineers that join the team should be able to find out about your chosen architecture quickly and easily.

Enforcing consistent styling

Once you have set some rules for how your styles should be written, you will want to enforce them. Whether this is done manually or via an automated process, you will want to ensure that the CSS in your codebase is written consistently over time.

Manually enforcing your code style

You can do this manually by checking new additions to the codebase when you are reviewing pull requests in your git workflow. If you can see that a new CSS rule is being added that uses the !important flag or creates new code instead of using an existing utility function that will give you the same outcome, you should probably advise that this code is changed.

It is optimistic to assume that every member of your team will have a complete understanding of your chosen CSS architecture. Humans make mistakes and it is impossible to ensure that every Pull Request will be reviewed without any code smells slipping through.

We need something more reliable.

Automating the enforcement of your code style

Much like we can use linters to review our JavaScript code, we can now do the same for CSS. PostCSS is a tool that is helping engineers write CSS and use plugins to improve or enhance it. PostCSS offers a host of plugins that include tooling to help us do all sorts of things to our CSS like auto prefixing, linting, minification and many more.

Automating your processes like this will help reduce the amount of issues that slip through your manual code checks and give the team more time to focus on the code that they are working with.

I would recommend finding out more about PostCSS by visiting the PostCSS repository.

Summary

However you choose to architect your project, you must ensure that you do not forget about your CSS. You must try and maintain a consistent style to your CSS code that makes it easier for new engineers to pick up and allows them to make changes confidently. Make sure that these rules are documented and enforced, preferably in an automated way.

The initial time spent creating a strong set of rules that govern the way you write CSS will far outweigh the problems that you might face in the long run if you don't invest this time now. You want new code to produce new features. Don't let small bugs take up the majority of your engineer's time and energy.

What new CSS architectures and tools are you discovering currently? I would love to hear your thoughts in the comment section.

Discussion (6)

pic
Editor guide
Collapse
jfbrennan profile image
Jordan Brennan • Edited

Tried them all for many years and I’ve switched over to a new pattern. I use custom HTML tags and attributes to define a meaningful API and apply a minimal set of styles to that component. When using a component, I sometimes apply utility classes to customize it further for a given design, for example:

Responsive Grid with one column getting a custom background and extra padding:
<m-row>
  <m-col span="3 sm-12" class="pad-all-lg bg-blue-light">
    ...
  </m-col>
  <m-col span="9 sm-12">
    ...
  </m-col>
</m-row>

Simple Badge component, some have customized font size:
<m-badge count="10"></m-badge>
<m-badge count="10" class="txt-sm"></m-badge>
<m-badge count="10" class="txt-lg"></m-badge>
Enter fullscreen mode Exit fullscreen mode

I like this because it doesn’t depart from the pattern set by native HTML and it’s just so simple - design your API (semantic tag and meaningful attributes) then apply the required styles for given tag+attribute(s). Utility classes take care of the rest.

And just to be clear: these are not Web Components or even Custom Elements. Browsers will happily parse any valid markup and create true DOM elements out of them. However, because it is good practice to prefix custom HTML tags (for a number of reasons), the browser considers candidates for Custom Element registration, but that doesn't mean you have to and not registering them has no negative effect. If you don't prefix you'll get unknown elements (developer.mozilla.org/en-US/docs/W...), but these are legit DOM elements too.

Collapse
ianholden profile image
Ian Holden Author

Thanks for sharing Jordan! That's a really interesting idea. I like how this pattern helps your HTML become semantically descriptive, given the use of well-named unknown HTML elements.

Collapse
jfbrennan profile image
Jordan Brennan • Edited

It has the added benefit of being able evolve these CSS-only components to JavaScript-enabled Custom Elements (or scale down in the other direction) without shifting paradigms and breaking changes. For example:

Using the attribute selector, create a green and red alert box:

<m-alert type="success">Success has success-like styles</m-alert>
<m-alert type="error">Error has error-like styles</m-alert>
Enter fullscreen mode Exit fullscreen mode

Pretend that we later decide we want to support an optional autodismiss="7" feature. The component has to now be implemented as a Custom Element, but all previous usages of Alert will not require any code changes because the constructs are the same, i.e. tag+attributes. And existing usages or new usage can simply add autodismiss like it was there all along:

<m-alert type="success" autodismiss="4">Now I disappear in 4 seconds</m-alert>
Enter fullscreen mode Exit fullscreen mode

It's worked very well in larger orgs with many products sharing a common design system.

Thread Thread
ianholden profile image
Ian Holden Author

Amazing! I'll have to try this one out for myself.

Collapse
mis0u profile image
Mickaël

As a back-end dev, this is a huge help for my future {S}CSS. Thx

Collapse
ianholden profile image
Ian Holden Author

That is great to hear Mickaël! Thanks for the feedback!