@mdo-ular CSS

Writing good, modular CSS doesn't have to be difficult. With a few guidelines, we can make CSS at any scale approachable and easy to maintain—for everyone.

Heads up! This is a derp in progress, so copy and code aren't completely done yet.

Written by @mdo, creator of Bootstrap and designer at GitHub.
Based on a previous talk given at jQuery UK 2015.


Simple class selectors

One of the most prolific ideals of good CSS today is the widespread use of classes in selectors. Classes are easy to read, perform great in browsers, and can be easily structured into components.

Sticking to classes for selectors normalizes the levels of specificity in your CSS. Since each class has the same level of specificity, the only thing that determines it’s ability to override other styles is that selector’s placement in the stylesheet.

Writing those classes well means abiding by a few structural rules. For example:

  • Use a single dash between important keywords.
  • Don’t chain classes—that increases specificity and dilutes the focus on building components.

Putting those to use looks something like this:

// Basic class structure
.component {}
.component-modifier {}
.component-subcomponent {}
.component-subcomponent-modifier {}

// In practice...
.tabs {}
.tabs-link {}

And in your markup:

<!-- Structure it like this... -->
<ul class="tabs">
  <li><a class=“tabs-link“ href="#">...</a></li>

<!-- Or like this! -->
<nav class="tabs">
  <a class="tabs-link" href="#">...</a>

Identifiable classes

Identifiable classes means keeping your classes easy to read, and more importantly, easy to find. Aim for obvious over clever.

Put another way:

  • Avoid common keywords. Searching for btn is easier than button because it matches an element, type, and role.
  • Abbreviate, but don’t create a new language. Folks should be able to determine the purpose of a class quickly.
  • Balance search-ability and readability. Keep classes short and legible before and after any compilation so you can more easily understand the output of your code.

For example:

// Not so good
.button {}
.header {}

// Better
.btn {}
.site-header {}

Base and modifier classes

Base and modifier classes are an important part of frustration-free and maintainable CSS. These classes help build families of components, reduce the number of overrides, and avoid unforeseen style collisions across components.

A base class is a required class a family of components that contains all the shared common styles. A modifier class is anything that adds onto that required class.

Take for example a typical family of buttons:

// Shared styles
.btn {
  display: inline-block;
  padding: .5rem 1rem;
  border-radius: .25rem;
  border: 1px solid;

// Variance
.btn-default {
  background-color: lightgray;
  border-color: lightgray;
.btn-primary {
  background-color: blue;
  border-color: blue;
.btn-danger {
  background-color: blue;
  border-color: blue;
.btn-success {
  background-color: blue;
  border-color: blue;

Written another way, say without a base class, you might have all buttons be gray to start, thus eliminating .btn-default. That’s less ideal because than every non-gray button will have overriding styles to make it not be gray.


Minimize overrides

Overriding styles is often overlooked as a sign of potentially problematic CSS. It’s not strictly a bad practice, but it leads to unnecessarily repetitive declarations and values.

  • Limit shorthand declarations as they usually pass unnecessary values throw to the browser. Declare only what you need.
  • Rely on modifier or utility classes to apply specific declaration-value pairings (e.g., .text-hidden or .margin-bottom-xl).
// Not as good
.element {
  margin: 20px 0;

// Better
.element {
 margin-top: 20px;
 margin-bottom: 20px;

Keep it CSS-y

Folks all over are finding that CSS isn’t as easy to write at scale. The cascade, inheritance, and global namespacing are all potential red flags depending on who you ask. As projects grow in complexity, CSS standards are often the first to falter.

Rather than rely on the dozens of preprocessor options available to you today, try to focus on writing simple CSS without all the preprocessor features. Avoid over use of extends, nesting, variables, functions, and the like to make CSS more approachable.

The additional layers and abstraction very quickly leads to clouded vision of what your outputted CSS will be, which is all your users and browsers care about (for bandwidth and performance).

Bottom line: CSS isn’t a programming language, so try to avoid forcing it to become one with additional layers of build tooling and abstracted code.

// Consider this…
.custom-btn {
  @extend .btn;
  margin: 20px auto;

  .container & {
    padding: 10px 20px;
// Requires .btn base class
.custom-btn {
  margin: 20px auto;

// Large buttons for landing pages
.custom-btn-lg {
  padding: 10px 20px;

Minimize nesting

Nearly every type of nesting with CSS preprocessors is unnecessary. It makes code more fragile, clouds your vision of the compiled CSS, and (usually) increases specificity.

There are several types of nesting techniques, but the ones that seem most helpful without those perpetual downsides include nesting pseudo classes and media queries.

For example:

// Good nesting
.btn {
  &:hover {}
  &:active {}
.element {
  padding: 1rem 2rem;

  @media (min-width: 30em) {
    padding: 2rem 4rem;

// Not so good nesting
.parent {
  .child {
    .element & {}

Property order matters

Every line of code should appear to be written by a single person, no matter the number of contributors.

// Property order
// http://codeguide.co
.element {
  // 1. Positioning
  // 2. Box model (display, float, width, etc)
  // 3. Typography (font, line-height, text-*)
  // 4. Visuals (background, border, opacity)
  // 5. Misc (CSS3 properties)



Embrace utility classes

  • Single purpose
  • Immutable declarations and values
  • Obvious nomenclature
.left  { float: left; }
.right { float: right; }

.text-danger  { color: red; }
.text-success { color: green; }
.text-warning { color: orange; }
.muted-link {
  color: #777;

  &:hover {
    color: #4183c4;
    text-decoration: none;
[hidden] {
  display: none !important;

Automate and track CSS

  • Linters and validators
  • CI tools with GitHub
  • Grunt and Gulp
  • Stats and reporting