Designing an Atomic CSS System

Making my CSS scalable from the outset.

Assumed audience: front-end developers and designers, especially if you’re interested in ideas like atomic CSS and atomic design.

You may notice in the URL that this is entry 003. That’s because I started this back on v4 of this site. Previous entries (retroactively numbered):

I’m starting building the visuals for the web view of rewrite, and I want this system to be easy to work on. While actively avoiding the infra engineer’s blind spot, I do want to engineer things well. I have concluded over the past few years that my preference is using (something like) Atomic CSS — with a slight twist. Atomic CSS typically emphasizes putting all those classes in your stylesheet. I prefer to use the atoms to define semantic class names instead, which represent a meaningful composition of those styles. This combines the reliability of an atomic CSS approach with the reusability of a more component-oriented design pattern. In other words, it combines the ideas of atomic CSS with the ideas of atomic design.

Here’s what this looks like in practice, as I build out the design system for the first time today.

11:45

I have just started. My CSS (technically SCSS) looks like this:


(That’s not a mistake! It’s just literally empty, because I just started. It will fill up in the bits below.)

12:03

I have some CSS now! In the interest of minimizing time spent on typeface choices today, I’m going with just using the system fonts, via this lovely setup. But it’s time for the first decision here: do I apply this to the body element, as a default? It’s definitely the safest, as then it just works” everywhere else. However, it also means that everywhere else has to explicitly override it. This is probably for the best, and it’s probably what I want in the system in general.

13:39

I’ve made no progress on this in the last 90 minutes, because refinancing a home is complicated and involves a lot of time with a spreadsheet and on the phone. I am, however, thinking about the appropriate mix of componentization” vs. just using the atomic styles directly… and how what I actually want is the ability to define a reusable block of styles which simply compiles to . This is the promise of many CSS-in-JS libraries, and it makes a lot of sense. On the other hand, maybe I solve that by just defining it as functions which return those sets of class names on the Elm side.

16:18

The refinance work squared away, and I’ve had a good long conversation with a friend familiar with both this project and CSS. My list of atoms is slowly growing:

.type-system {
  font-family: system-ui, sans-serif;
}

.fs-base {
  font-size: 16px;
}

.m-0 {
  margin: 1rem;
}

.p-0 {
  padding: 1rem;
}

It currently gets applied to the app like this:

bodyClasses : Html.Attribute msg
bodyClasses =
    class "type-system fs-base m-0"

I have no idea whether I’ll end up keeping exactly these names (spoilers: I probably won’t), but the point today is to make progress. And I am!

And my friend — humorously and kindly but not inaccurately described CSS as Crazy Stupid Stylesheets: It’s wonderful, but also it’s crazy and kind of stupid.”

17:04

I added these lines to my atoms — 

.grid {
  display: grid;
}

 — and I immediately began to think about what the grid system needs to be. (I’m perfectly content only supporting browsers which support CSS Grid with this app. There’s no reason not to, in fact: IE11 will be well and truly dead soon.) I need a grid system, with well-defined defaults for gaps, reflow sizes, etc.

17:27

It’s time to go eat dinner, but I’ve made some progress: borrowing ideas from Tailwind and Tachyons, I’ve started building out a grid system I find reasonable. I now have a bunch of declarations like this:

.grid-cols-12 {
  grid-template-columns: repeat(12, minmax(0, 1fr));
}

@media screen and (min-width: 480px) {
  .med--grid-cols-12 {
    grid-template-columns: repeat(12, minmax(0, 1fr));
  }
}

@media screen and (min-width: 960px) {
  .wide--grid-cols-12 {
    grid-template-columns: repeat(12, minmax(0, 1fr));
  }
}

@media screen and (min-width: 1440px) {
  .x-wide--grid-cols-12 {
    grid-template-columns: repeat(12, minmax(0, 1fr));
  }
}

Those get applied like this, now:

bodyClasses : Html.Attribute msg
bodyClasses =
    class "type-system fs-base m-0 grid grid-cols-none wide--grid-cols-12"

This makes it not use a set of grid template columns on smaller screens, 4 columns on medium” sized screens, 8 columns on wide” screens, and 12 columns on extra-wide” screens. Note that this is a mobile-first approach! In my experience, if you start mobile-first, responsive design is if not easy then at least very tractable. If you start by assuming a large screen, designing back down to mobile is very hard.

While this works reasonably well, I may also want to set a minimum size on those columns. We’ll see. I also don’t know yet if these sizes will work exactly right!

The other important thing to notice here: many people use this kind of responsive grid to make a fluid grid system where things reflow dyanmically. This isn’t quite that: I’m aiming for a responsive flow, but not a totally fluid flow. Totally fluid flows work much better for content-oriented, rather than application-like web interfaces.

17:41

That’s a wrap on this particular session. Hopefully it was somewhat interesting and illuminating for you; I enjoyed documenting my thoughts this way as I did it. It actually helped me keep making progress all day, even if the progress was slower than it could in principle have been!