How I Learned to Stop Worrying and Love the Cascade

Some of my thoughts about best practices for maintainable CSS used to style HTML.

Published in ·

I think, in general, the implementation of graphic designs in CSS can be made a lot less complex. Here is (at least what I believe is) a simpler approach to managing it. CSS is definitely a step up from <font> and <table> designs, and is one of the most-used "languages" that exist; mainly because of its declarative simplicity. Anyone can sit down, having never seen it before, and start making changes and be productive in minutes. That's a seriously big deal. CSS is awesome that way. But CSS doesn't need to be a full-blown programming language either. It has a lot of great features built-in. Rather than fighting it, I think we should all learn how to embrace the cascade!

Web != Print

I'm a full-stack developer/designer by trade, so when I start a new project I'm not usually thinking about CSS right off the bat, but I must confess: applying styles and implementing a graphic design in CSS is the part of any project that I look forward to the most. I personally find it the most relaxing, straight-forward, and impactful (from an observing user's point of view) part the the process. It's where the app has life breathed into it; when it starts looking like something. You first see an app's magic happen here.

I've talked to a lot of devs and they think I'm crazy. They say things like "Why doesn't CSS have programming feature X? Why are all these styles overriding other styles? How are you supposed to get anything done? Why can't I OOP this shit up?". I think a lot of this sense of dread comes from a misunderstanding from both designers and developers. I see designers harassing developers for pixel-precision to match their fabulous designs and I see developers cutting corners in markup, wrapping each and every part of a UI in useless <div>s just to namespace a little section so they can clobber all the other styles being cascaded down the hierarchy.

I think everyone involved just needs to step back, take a deep breath, and accept the cold hard fact that when it comes to web designs, flexibility is the name of the game, not pixel-perfection. You have to know that your design is going to shift and change slightly on slightly different systems, on different resolutions, and in different lighting conditions. You have to create with this in mind. I know we're over 20 years in, but it seems like it still needs to be said: the web is not print!

Only Say What Needs to Be Said

The most perplexing thing I see when I look at other people's CSS isn't even the CSS itself, but the markup that it's trying to style. "Semantic" means: it means something. The whole point of HTML is to mark up content to give it context and meaning so that when computers look at it, they see a nicely structured and meaningful collection of concepts. Markup should only be introduced if it adds meaning, clarity, specificity, and/or definition to the content. You should never be adding it for the sake of styling per se (although during the process of styling content, you may discover some ill-defined areas of your markup which could use some extra separation). It is the meaning--the semantics--that you are expanding upon, not the styling.

As you might imagine, my own opinion on CSS involves an absolute minimum of markup (only use what you absolutely need to express the meaning of the content). Similarly, I also believe in short, concise, well-expressed class names (I have the exact same opinion about variable names in general) such that in looking at the raw markup, even a lay person could make well informed judgements regarding the purpose of those words (e.g., just call a class "signup-form" rather than "form form__signup--new" or "column-2 grid-4 span-half sidebar"). I believe in DRY and KISS, and I believe in minimalism in general (if you can express the same thing with fewer words, without losing meaning, I think the shorter method is always better).

As a general rule, I feel that UI should never take center stage in an app. It should always sit back and be as invisible as possible, only presenting itself when needed to empower users to solve their problems (now, this might mean having a button always displayed regardless of app state if that's what's needed, but as a rule: UI designs should, imho, always strive to introduce as little UI as possible, step back, and let the user use the app, focusing on content). I think common sense will always win the day. As a result, I'm not really a big fan of things like BEM or Bootstrap or PostCSS or what-have-you. When it comes down to it, CSS is pretty darned good at solving visual web presentations on its own.

Take All This With A Grain of Salt

Organizing large application styles can be daunting and requires a bit of forethought so you don't get lost in a tangled web of overriding overrides that are all !important. The architecture I'll lay out here will be, I hope, a guide that can help you organize your styles by meaning.

Before I get started, I should mention that I don't use plain vanilla CSS (I like SASS; and LESS is pretty much the same thing). There are certain annoyances such as vendor prefixes, no modularity, and lack of variables which make certain details more difficult when using vanilla CSS. That said, the SASS I make is very minimal. I use the SCSS syntax (which is pretty much vanilla CSS) and only make use of variables and @import (and maybe a convenience method from time to time, such as darken() or lighten()). This just helps me structure my code and stay DRY, that's all. I don't go crazy with mixins or creating a convoluted OOP hierarchical taxonomy or anything (fyi, don't do that), so I think it's really pretty simple and easy to understand.

I don't mean to say that the structure I'm defining here is the be-all and end-all of CSS organization, nor should it be followed to the letter. I just find this structure makes a lot of sense to me. It feels natural and organic; it can adapt to changing circumstances and meet oncoming challenges. Maybe it can help you too. I'm constantly re-evaluating how I work and revising how I do things. You should too. Always be thinking! Take this as a starting point, but adapt it to the way you work. If you have some good ideas, let me know!

High Level Categories

Let's start with folder structure. I like to create a folder called "styles" in the root of my project from which I can make use of build tools (like with Gulp, Grunt, Webpack, or whatnot) to pull in my SASS and output the resulting vanilla CSS to another build folder for use in production.

Inside this base folder, I then create one or more app-specific folders. For example, for a particular web app, I may have a folder for "client" (perhaps a public-facing SPA), "admin" (a different, locked-down SPA used only by administrators), and maybe another one called "marketing" which is a separate set of styles for the static marketing website which promotes my "client" app. These are the highest-level buckets, grouping styles into logical sub-apps which are all related in a single project. Inside each of these high-level buckets I use the same repeated structure to organize per-app styles.

Inside each app folder I then create an index.scss file which acts as the root for each app's stylesheet. This file does nothing more than import all the other styles in the order in which they should cascade. This file gives you a broad overview of the app styles and makes it easy to re-order things to help with cascades and overrides (e.g., you might use a default app-wide style at the top of the file which is overridden by more specific component styles further down).

An example index file might look like this:

@import "vendors/normalize";
@import "vendors/ionicons";

@import "helpers/colors";
@import "helpers/measurements";

@import "core/defaults";
@import "core/layout";
@import "core/fonts";
@import "core/shared";

@import "components/header";
@import "components/page";
@import "components/forms";
@import "components/flash";
@import "components/chat";

@import "animations/spinner";
@import "animations/progress-bar";
@import "animations/slide-down";

As you can see, the styles are broken down into logical buckets which can easily be arranged as you see fit simply by editing this file. The things that are imported closer to the top will be overridden by things that are imported closer to the bottom. I should also mention that in SASS, any variable that is imported before another file will make that variable available in that other file. This way you only need to import those variables once, here, and then just use them in all your other files ;)

Folder Structure

Along side the base index.scss I then include a standard set of folders which I use for each and every app/stylesheet I want to create. It looks something like this (here, I'm only showing the admin stylesheet, but the same things would be done for each stylesheet inside client and marketing too):

styles\
  admin\
    animations\
    components\
    core\
    helpers\
    vendors\
    index.scss
  client\
  marketing\

This is the basic structure. There is a styles folder which has one or more children, each of which represents a single stylesheet (for example, per app/website/section). Each of these folders uses a standard internal structure to help organize the different parts of each stylesheet, each containing a main index.scss file which will be compiled down to a single vanilla CSS file by your build tasks (however you want to do that).

As you can see, the names of these internal folders are fairly self-explanatory. I start with a core folder which includes any global or shared styles which will apply to the stylesheet as a whole (e.g., defining the base font, the size of headers, and the general, broad-strokes of the layout. I then make use of some helpers which include re-useable variables for things like color palettes, grid measurements, and fonts. Next to that is vendors which contains 3rd-party CSS which I might use like resets, icon sets, etc. Most of the work will likely be done in the components folder which contains separate .scss files for each logical "component" in the UI (e.g., things like "header", "lists", "forms", and more specific widgets like "switch", "suggestion-input", or "signup-button"). The idea is to group styles by logical components or problem domains; little pieces of the UI that group a bunch of markup into a nameable category. I don't have any hard-set rule on how to divide things up, but it's like anything in programming when you're trying to name variables and segregate modules. I don't think it's possible to define some universal rule on how to do it. Just use common sense. Lastly, there's the animations folder, which I keep separate because I find CSS animations can get pretty big and verbose, so I like grouping those into their own files (e.g., "loading-spinner", "progress-bar", "page-flip", etc.) Each file in this folder would correspond to a single animation which could be applied to an element with a class name.

This is what a fully populated admin stylesheet folder might look like:

admin\
  animations\
    spinner.scss
    progress-bar.scss
    slide-down.scss
  components\
    header.scss
    lists.scss
    switcher.scss
    buttons.scss
  core\
    defaults.scss
    fonts.scss
    layout.scss
    shared.scss
  helpers\
    colors.scss
    fonts.scss
    measurements.scss
  vendors\
    ionicons.css
    normalize.scss
  index.scss

Core

It's worth talking a bit more about the core folder specifically because the files in here are always the same. These are meant to be global styles which would cascade down through all the others; things that would always apply except in very special circumstances.

defaults.scss

This is usually the first thing to be loaded by index.scss (after any resets and whatnot). In this file I try to only define base HTML tag styles (that is, no classes are defined here). These are styles that apply app-wide which define the core essence of the app/stylesheet. I might include things like applying box-sizing: border-box to all elements, or define the font-size and line-height for the <html> and/or <body> tags (which would cascade down to all children unless overridden specifically). This, thus, also defines the size of an em or rem which will be used for layout purposes deeper down. I usually define a set of header tag sizes here for <h1>, <h2>, <h3> etc. so that they are always consistent. I'll define the <a> tag's style to standardize all links. I'll define how <b>/<strong> and <i>/<em> will look (e.g., I might use an actual italic font rather than a browser's fake italicize function). I might set some basic <button> styles (like remove the default 3d shadow stuff), I might do the same thing for <input> and <textarea> as well. The point is, define your root, app-wide, generic styles here. Think of this file as an app-specific reset which sets up all your subsequent styles so they start off on the right foot.

layout.scss

I like using this file to store the broad strokes of my layout. The stuff that doesn't change. But I don't apply any "decorations" here, only layout styles (i.e., no colors, fonts, or borders; only display, position, overflow, padding, and margin for the main parent containers of the UI). I find this helps me "see" the structure of the app's layout at a glance which makes it easier to later make adjustments for things like responsiveness. So, for a classic layout like this:

==========================================
|                 header                 |
==========================================
|                   nav                  |
|----------------------------------------|
|                              |         |
|                              |         |
|                              |         |
|           content            | sidebar |
|                              |         |
|                              |         |
|                              |         |
|                              |         |
==========================================
|                 footer                 |
==========================================

I might define each of these sections in the layouts.scss file where I simply apply styles like display: flex and flex-direction: row etc. which simply place each of these containers in the locations of the viewport that I desire in order to structure this general layout. I won't define smaller widget layouts here, only the broad-strokes which apply to the app-as-a-whole.

<body class="my-app">
<header></header>
<main>
<section class="content"></section>
<nav></nav>
<aside></aside>
</main>
<footer></footer>
</body>
.my-app {
display: flex;
flex-direction: column;
min-height: 100vh;

& > header {
}

main {
display: flex;
flex: 1;

.content {
flex: 1;
}

& > nav,
& > aside
{
flex: 0 0 12em;
}

& > nav {
order: -1;
}
}

& > footer {
}
}

shared.scss

I put a file in here called "shared" just as a placeholder for any re-usable, high-level classes I might want to use in my app that don't necessarily belong to any individual component, or for which I may want to override with component-specific versions of the same class. Things like "button" or "divider" or "disabled". To be honest, I don't use this file that much as most of these things make more sense to me as their own "components" anyway (e.g., I'll make a buttons.scss file in the components/ folder for buttons and import that above all other components which contain all button-related classes and styles). But I find this file handy as a placeholder while I'm developing, while I haven't fully fleshed out what my style structures are yet. So I might make a little .button class in here, first, and later expand that into a full-fledge component file.

fonts.scss

This is just a place to put all of my @font-face definitions. These are usually pretty verbose and deserve their own file to contain them all. Also, by placing them in a high-level file, here, they can be imported, in order, above all other components in the index.scss file.

Keep It Simple, Stupid (KISS)

I find that in most circumstances there really isn't even a need to create a new CSS class where a plain HTML tag name can be used. I think devs should really take more advantage of what's already there. HTML has great semantics you can latch your styles onto right out of the box. Don't over-think things!

For example, rather than doing this:

<div class="my-component">
<div class="my-list">
<ul>
<li>
<div class="my-list-item"></div>
</li>
</ul>
</div>
</div>
.my-component {
.my-list {
.my-list-item {
}
}
}

just use what God gave you:

<div class="my-component">
<ul>
<li></li>
</ul>
</div>
.my-component {
& > ul {
& > li {
}
}
}

Take advantage of the semantics that are already there! Use newer tags like <main> and <aside> rather than <div class="main-content"> or <div class="sidebar">. Use combinators like >, +, and ~ to create specificity without adding more crufty classes (think hard before you create a new class). While you're at it, use attribute selectors like input[type="checkbox"] and pseudo-classes like :last-child, :focus, and :checked too. Stop using float and table; use flexbox (anyone using IE9 should have their internet license revoked). Don't use px for measuring things (unless you have a good reason); use em or rem based off a standard grid size. Use box-sizing: border-box because anything else just doesn't make sense (at least to me). Think about how your design will resize with the viewport (this isn't static print!). Be careful not to nest things too deep in your SASS/LESS (it's really easy to forget about it; define things from the root as much as possible). Don't use !important; if you're using it, you're probably doing something wrong (there are exceptions, but in general it's bad form). Try to use the same measurements to position things (e.g., use a grid of, say, 4 pixels and base all your measurements off that such that elements are positioned in multiples of 4 pixels). A good design should have a well-defined grid which should make the CSS a no-brainer. If you find yourself shoving stuff around by 1px increments using floats and absolute positions, this might be an indication that the design needs revision, not your CSS ;)

The Web is Fuzzy; Design for Flexibility

I'd like to yell at all you graphic designers out there for not understanding how the web works. I see poor developers being told to push things, one pixel at a time, spending hours and hours revising minuscule little things just to force a website to conform to the designer's internal sense of what's "right". Look: shit's gonna move around and be slightly different on different platforms. Know this going in, and work with it, not against it. Stop wasting time trying to turn the web into print and let developers actually make stuff work instead of churning on CSS that nobody will likely notice because there's so many other inconsistencies in your design already.

Don't get me wrong, I absolutely love good design and I truly believe it's one of the most important aspects of any good app: it's the part people touch and feel and love. But it's not more important than an app that actually does something. I don't care how great it looks if it's broken! Balance and flexibility are key. Trade off pixel-precision for minimizing bugs.

Talk to your developers and listen to them; they know stuff. Also, learn CSS yourself and push pixels 'til your heart's content. Ultimately, the final CSS should be an iterative process of trial and adaptation which transforms your ideal design into the real design based on a healthy back-and-forth between graphic design and CSS implementation. Your job is to define this ideal, not to enforce strict adherence. Work together to discover the best way of representing the solutions to UX problems through visual communication in the web medium; this means actually using the medium of the web, not InDesign.

References