Building AIM, Part 4: CSS Architecture

We needed to build an inventory system, one that was free from the restrictions of our legacy system. We wanted to build a system that could describe any piece of inventory: from cars to carpets, from houses to job listings. We needed an interface for our sellers to actually manage that inventory. That interface is the AutoConX Inventory Manager, which we call AIM.

This is the story of the CSS architecture for AIM.

For most of the CSS work I do, I serve as web designer and front-end developer. I have enough experience and knowledge of web design and UX design to handle most features on my own. When we start a project from scratch, however, our web designer is brought in to help establish the look and feel. We work in close collaboration. I think of our skillsets as having the same tree trunk but hers branches out into Photoshop and InDesign, and mine branches into HTML and CSS.

That design looks familiar… When we were in the research phase, I recalled hearing about an ambitious new design project that Google had launched just months earlier. I suggested to our web designer that we look at Material Design for inspiration. She agreed, and we would often just reference components or patterns from the guidelines during our development. This gave me the liberty to implement components according to those examples rather than relying on internally-produced mockups. At the time, there was no Material Design Lite, so I poured over the documents and built our own implementation.

What CSS methodology do you use? I was inspired by CSS methodologies like BEM but found some of the rules pedantic. Also, who wants to type that many underscores? I began working on my own version of BEM that I called ABC (AutoConX Block CSS). It has many of the same ideas: break UI into named blocks, break blocks into elements and use modifiers for alternate versions. ABC uses double hyphens as separators, uses verb-prefixed modifiers (like `.is-hidden`) and makes use of utility classes prefixed with `.u--`. Aside from normalize.css and a few exceptions, all selectors are classes. Whenever possible, selector specificity is one class (0 0 1 0). In some cases, an ancestor is used, but that is usually in the case of parents with modifiers. I never use IDs, never get important and never get too specific (unless I’m dealing with print styling, in which case it’s important that you use your ID to be me a specific bottle of whiskey).

You seem to have a lot of rules. Yes. I take my CSS seriously. As a result, I use Stylelint to ensure that all CSS is written in the same manner. When I implemented Stylelint, I went through the documentation, rule by rule, handcrafting our settings in my evil design. The flexibility and specificity that Stylelint offers is fantastic for small teams where other developers don’t read the Wiki I spent so much time writing.

What preprocessing do you use? I’ve tried Sass, LESS and Stylus, but I’ve had hangups with all of them. My choice with AIM was either to try something new (and have to live with it) or to go back to one of those three. Then I ran across cssnext, and I knew it was perfect. cssnext is a bundle of PostCSS plugins that provides future-facing CSS for developers. The CSS you write comes from bleeding-edge specs (custom properties, media query ranges, color()), and it gets processed into styling that browsers understand today.

I fell in love with the idea for a couple reasons. First, I can CSS using some of the killer features that other preprocessors boast. Second, the syntax is familiar, because it’s still CSS. No one on the team has to learn a new language to write or read AIM styles. In fact, developers don’t have to know about cssnext at all. They can still write CSS as they always have. It’s even possible that one day the processed file will look identical to the source file.

How do you handle regressions? Just as it was important to me to test the CFML in the application, it was also important to test the styling. I had gone through the work of creating parameterized views, so it was easy to stitch them into a living Style Guide. The guide showed me at a glance all the different design patterns in use. When planning a new feature, I could consult the guide to determine what existing elements could be utilized. This, on its own, was extremely helpful.

Using PhantomCSS, I developed a task in Gulp that would ensure visual regressions were checked and statistically-relevant discrepancies were reported. Every UI update or addition would very clearly illustrate any side effects. Regressions were rare due to using shallow specificity, but this task greatly increased my confidence with each new deployment. It also made refactoring HTML and CSS nearly bullet-proof. By tying this task into my precommit and prepush hooks, I further protected myself against deploying code that broke existing UI. This has become my gold standard for CSS development going forward.