We've been going about this all wrong!

  • OOCSS
  • BEM
  • SMACSS
  • SUITCSS
  • AtomicCSS
  • Bootstrap
  • Tailwind

One of the things that link all these tools is that they all seek to tame complex CSS. Another thing that links them is that they all fail at that goal to one extent or another.

Ok, that's a little provocative. It's also not quite true. It's not that these tools have failed us. The real problem is that we have failed to understand CSS at a very basic level. As long as we continue to do that, no tool will really be able to fully meet our needs at scale.

How's that for provocative?

CSS is likely not what you think it is

When I first learned frontend development, I was taught that there were three primary concerns; structure, style and behavior. And those concerns had corresponding languages to address them, namely HTML, CSS and JavaScript respectively.

This was great! One did not need to develop an intuition about the boundaries of concerns. If a file had a particular extension, it necessarily dealt with a particular concern. No additional thought needed!

It makes sense! It made so much sense, no one thought to question it for a very long time. But new paradigms have a tendency to reveal the boundaries of our previous paradigms.

Then an age of componentization was ushered in with the initial work of a standardized WebComponents specification, and frameworks such as React. The entire industry followed suit.

The Angular team, famously already deep into a rewrite of their massively popular framework, pivoted mid-stream to be fundamentally component oriented.

Component orientation came to CSS in the form of several methodologies including OOCSS, BEM, and SMACSS. These ideas sought to compel a form of modularity to CSS through discipline.

.block {}
.block__element {}
.block—modifier {}

Soon however, CS-in-JS came along to compel the same thing through code and gave us true componentized UI. Implementation of the WebComponents specification with ShadowDOM and scoped styles began to accomplish true modularity at the platform level as well. Throw in tools and technologies like Sass, CSS Variables, and the like, and it seems like we should have attained CSS transcendence by now.

So why do we still struggle with CSS at scale? It’s almost like we are missing some fundamental truth!

To be fair, what we are about to discuss is not really new at all. The idea has been teased, hinted at, presumed, or stated outright ever since at least the introduction of OOCSS by Nicole Sulivan back in 2005!

What I hope to do is not to bring forth a revolutionary idea, but rather to add a small contribution to the incredible work of those that have preceded me by tweaking our perspective just a bit. I hope that in so doing, all of us can go forward with a more accurate sense of things that will provide clarity and simplicity.

A slight correction

To properly understand, we need some background, some context, and a correction.

First the correction.

I said that when I was first learning frontend development, the three primary areas of concern were Structure, Style, and Behavior which were handled by HTML, CSS, and JavaScript respectively.

This was never quite true.

Certainly Style was handled by CSS, and behavior by JS, but HTML is not structure. At least, not as it pertains to how the user perceives it.

HTML provides semantic structure, which really means it deals with the meaning of the relationship between objects — one thing is a child of another, or this thing represents an external resource, or a block of text, or an item in a list.

Semantic structure has nothing to say about how a user consumes such things, only how those things relate to each other. Ideally there is a loose relationship between the semantic structure and the visual structure of things. There are a ton of multiplying advantages to that being true, but it is not necessary.

Consider a well formatted pice of HTML. It has all the right structure and semantics. It describes the object, has the proper accessibility attributes, but it's style is set to display: none;

<figure style="display: none;">
  <img src="https://fillmurray.com/400/600" alt="A beautiful Bill Murray placeholder image" />
  <figcaption>
    A beautiful Bill Murray placeholder image
  </figcaption>
</figure>

What does the (typical) consumer of this experience? A ghost. A blank slate. Nothing at all.

Conversely, consider Louise Flanagan’s CSS rendition of Vermeer’s “The Girl With the ~~Pearl~~CSS Earring”.

The divs used as the canvas for this masterpiece provide no semantic relationship to anything we see.

They are in fact, utterly ambiguous to what they are used for. Yet one cannot help but feel a sense of wonderment and awe when looking at it.

So if HTML does not provide a sense of visual structure, what does?

Double Duty

It turns out that CSS is really pulling double duty here, and that is the secret, the missing piece of the puzzle.

Without understanding that one language is solving for two entirely different concerns, we are bound to end up in a hot mess of CSS soup given enough time and cooks in the kitchen.

I say that’s the secret, but it’s been right in front of our faces this whole time. We have just been too busy not paying attention.

CSS (Cascading Style Sheets) is used to style and lay out web pages — for example, to alter the font, color, size, and spacing of your content, split it into multiple columns, or add animations and other decorative features. This module provides a gentle beginning to your path towards CSS mastery with the basics of how it works, what the syntax looks like, and how you can start using it to add styling to HTML.
CSS (Cascading Style Sheets) is used to style and lay out web pages — for example, to alter the font, color, size, and spacing of your content, split it into multiple columns, or add animations and other decorative features. This module provides a gentle beginning to your path towards CSS mastery with the basics of how it works, what the syntax looks like, and how you can start using it to add styling to HTML.

While HTML is used to define the structure and semantics of your content, CSS is used to style it and lay it out.

That CSS is doing two things for us is not a problem, per se. In any other development platform, there is typically only one language to address all concerns. This is managed by applying architectural patterns such as MVC, Functional programming, OOP, and many many others, depending on one’s needs.

And we have those patterns too! We have just been looking past what they are actually solving for us, the need to segregate the structure from the style of our web applications.

Don't Stop!

Careful now! This is where everyone else has stopped. If we stop now, we haven’t learned anything that isn’t already widely known.

What’s the next step? Where is the tweak? What’s the perception shift that radically changes what we do?

Left with only this information, what has tended to happen is that a bunch of style only components get created, and then someone has a lightbulb moment and is like I’ma make Grid component, or some other Layout component.

I'ma make a component! 🤯 😍

And why not? If CSS is handling two concerns, creating separate components for style and layout is the next logical step. Folks much smarter than me have made cases for this.

I have a great deal of sympathy for this inclination, but this is not the way. It only leads to suffering.

Layout components lead to anger, anger leads to hate, hate leads to suffering.

In all seriousness though, layout components just complicate things. One ends up with so much extra and unnecessary stuff! Now we are sacrificing the semantic structure HTML is supposed to provide to force it to give us the visual structure we are seeking. It’s div soup all over again. It’s not necessary.

div soup
div soup

We need a way to use CSS to give us the visual structure we need, without destroying the semantic structure of the underlying HTML.

Let me suggest a simple mantra that allows us to keep a proper separation of style from layout within CSS, allowing our components to be freely portable, without the unnecessary cruft and circumstance.

Components style themselves, and position their immediate children.

👆This is it. It’s all you need to know to begin immediately creating more resilient components, whether you are using BEM or Sass and have separate CSS files, or you are using CSS-in-JS or Web Components to co-locate relevant component code, this mantra will ease your pains.

A mantra is only as good as our understanding of its implications though, set’s really look at what this means in practice.

What is style, and what is position? I’d like to get into some really interesting cross discipline discussions of structure vs ornamentation, but for the sake of time, when it comes to CSS, just think of the box model.

The CSS box model
The CSS box model

Anything that affects only the border-box inward, is style. Everything else is position.

There’s another easy thing to remember!

Anything that affects only the border-box inward, is style. Everything else is position.

Pop Quiz

O.k. Let's see how well we understand this principle. Below is a little pop quiz. Listed is a CSS rule property. Your job is to determine whether that property affects style — and can be safely applied directly to an element, or whether it affects position — and should be applied by its immediate parent. Expand the dropdown to check your answer.

color style
padding style
margin position
border style
display Well, it depends 😏. Some values, such as `grid`, and `flex` are known as "[display-inside](https://developer.mozilla.org/en-US/docs/Web/CSS/display-inside)" values and affect the layout of their immediate children. By definition, these rules can safely be applied directly to the element. For our purposes then, these can be considered style rules.

Other values such as block, inline, and run-in are known as "display-outside" values and affect how sibling elements interact with them. For our purposes, these can be considered position rules.

flex position
grid position
width/height position

Show me some code already!

What does this look like in practice? Stay with me here, because this part could get a little controversial. I’ve found the easiest way to write CSS that demonstrates this principle is by using the immediate child selector.

.component {
  & > .component-part {
    margin: auto;
  }
}

Let’s get a good look at this. We’ll start simple, with a button.

We can move this button around it’s container using margin (give it a go), but as soon as we add more buttons, they inherit those same styles (try it!). Maybe that’s what we want, but probably not.

In any case, as soon as we put a button in a different container, all kinds of weird stuff starts happening. And heaven help us if we make assumptions about the display type of the container we expect a button to be in and add an align-self or similar.

You see, the common cause of the problems here are assumptions about positioning. Things only the containing element actually know cares about.

If we move that responsibility to the container. All that context can be taken out of our generic component, and it can be brought into any container with no pretext, other than being what it is, a button.

The container is now responsible for laying itself out. After all, it’s the only one that knows how it’s supposed to layout.

I hope this makes some sense. If not, and you want to talk further, or you're a nay-sayer, want to debate me on the merits, or just want to give some of those solid burns you've been crafting to generate twitter likes on me, hit me up. Let's discuss.

If all of this is too much to hold on to just now, just remember three things.

  1. Components style themselves, and position their immediate children.
  2. Anything that affects the border-box inward, is style. Everything else is position.
  3. Layout components are NOT the solution.

Happy CSSing!


This was originally given as a conference talk at UtahJS in December of 2020. It has been lightly edited for the written medium.