Component Independence

And why it can be hard to do in CSS

This is an excerpt from a rewrite of Scalable and Modular Architecture for CSS (SMACSS) that I’ve been working on.


There are a number of reasons why creating independent modules with CSS can be difficult.

  • style inheritance
  • the cascade
  • the browser’s differing behaviour between HTML elements
  • the required interaction between modules

Style Inheritance

Some CSS properties are inherited. While this can affect the independence of a module, inheritance usually works in our favour.

If we had to set `font-family` on every element, we might’ve given up on CSS years ago. We wouldn’t have been much further ahead than the font tags we were trying to move away from. Instead, we can set it on the `body` and most elements will automatically inherit the style. This is a good thing.

CSS inheritance has the biggest impact on typography. Properties like `color`, `font-size`, and `text-align` are inherited. Properties like `border-size` are not.

(Other CSS properties are also inherited but mostly pertain to list and table styles designed to allow child `li` and `td` elements to inherit properties from the parent `ul` and `table` element respectively.)

Cascade

The cascade is often looked at as a big benefit of CSS. It’s in the name, after all. They’re Cascading Style Sheets! Some aspects of the cascade are good. Others, however, like when we are fighting specificity, are a sign that we have multiple rules trying to do different things to the same element.

There are different aspects to the cascade but the one we care about is specificity. Specificity is the decision making process that the browser uses to decide which of multiple CSS rules will apply to a given element.

a { color: blue; }
.subdued { color: grey; }
#danger { color: red; }
<a class=”subdued” id=”danger”>Cancel</a>

In this example, we’ve created three different rules and applied them to the same element. This layering of multiple rules on the same element is how we get ourselves in trouble.

Did I want that link to be red or did I want it to be grey? We could find ourselves adding more CSS to answer this question. Maybe putting `!important` somewhere or creating a `#danger.subdued` selector to answer that question.

Different Default Browser Behaviour

Just as we have to override our own styles when we overlay multiple rules on the same element, we have to override the styles that browsers set by default on elements.

For example, let’s create a horizontal navigation.

<a href=”…”>Home</a>
<a href=”…”>About Us</a>
<a href=”…”>Contact Us</a>

With a simple collection of links, we don’t have to write any CSS. The links are, by default, inline and will appear horizontally on the same line as long as there’s space for them.

What about if we decide to use `div` tags?

<div><a href=”…”>Home</a></div>
<div><a href=”…”>About Us</a></div>
<div><a href=”…”>Contact Us</a></div>

The browser sets a `div` to be `display:block` by default, stacking the elements instead of letting them appear beside each other. Therefore, we have to add CSS to override the browser’s default styling.

div { display: inline; }
/* or inline-block, or flexbox, or float */

A more common navigation pattern is to use a list. Let’s change our markup to that, instead.

<ul>
<li><a href=”…”>Home</a></li>
<li><a href=”…”>About Us</a></li>
<li><a href=”…”>Contact Us</a></li>
</ul>

A list item, like a `div`, is `display:block`, by default. We will need to set `display:inline` (or, ahem, inline-block, or flexbox, or float) but we have another problem. The `ul` and `li` introduce some other default browser styling — such as list bullets — that also needs to be overridden.

ul { list-style:none; padding:0; }
li { display: inline; }
/* or inline-block, or flexbox, or float */

As you can see from this example, it’s hard to write CSS that is truly isolated and portable. It’s dependent on the HTML that you use since browsers define different styles for different elements. The properties you define for a link are going to be different than the styles you define for a button to achieve the same effect.

When building modules, we’ll be tying CSS to an expected — although possibly still unpredictable — HTML structure. We’ll want to ensure that our CSS only impacts the HTML that we care about.

Interaction Between Modules

Developing independent modules can also be difficult because we often have to make decisions based on the interactions between modules or between multiple instances of a module.

For example, we can style up a button to be its own and independent piece but what happens when you want two buttons next to each other? How do you decide how much space exists before, after, and between items? Now what happens when that button sits next to text or an input? What if you have controls that are a combination of other components? Like an autocomplete that uses an input and a dropdown and a button.


All of these concerns have different ways that we can solve the problem. I don’t think there’s only one way to solve the problem. SMACSS, in its original incarnation (and its brethren, BEM, OOCSS, etc), tried to solve some of these.

Simple class selectors and module isolation, for example, can address the cascade. Defining typography at the module level can address inheritence. Templating (e.g. Mustache, Web Components, React) and the new CSS property `all` can address HTML independence.

The one problem that I don’t see a lot of people talking about or addressing is how to easily define what happens when modules are placed next to each other. This is something I intend to go into more depth on in the new edition of SMACSS.

Like what you read? Give Snook a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.