CogoBlog

How to Write Better CSS for Teams

In the Cogo Labs incubator, there are always a number of different startup companies (and ideas for startup companies) at various stages of development. Almost all of those projects require a web presence of some sort. It might be a small, one-page site, or it might be a complex, JavaScript-driven web app. Either way, we use CSS a lot, and it’s vital to our fast-paced workflow that our CSS be as lean and efficient as possible, so that everyone on the team can understand at a glance what each line of code does.

cogo coding octopus
Illustration by Lauren Fisher

How We Write CSS

Unlike most web development languages, everything in CSS exists globally, and each new rule has the potential to cause unintended changes to other parts of the design. Anyone who’s worked in web development has seen theoretically "simple" projects quickly become cluttered with dependent code. Our methodology gives us control over specificity issues while keeping our code organized, readable, and efficient. We do this by:

  • naming our classes using clear, self-describing class names;
  • keeping our specificity level the same across all of our style rules;
  • restricting lengthy selector lists;
  • extending CSS functionality with preprocessors or JavaScript plugins; and
  • setting team coding standards for consistency.

Self-Describing Namespaced Class Names

Our naming convention is similar to the popular CSS methodology BEM (block-element-modifier), which helps us constrain the scope of a style rule to a modular element, while also providing meaningful class names that make it really clear where in our markup a style rule is applied. 

We place a class name on a module’s outermost container that is simply named after the module itself:

<nav class="nav">

We give child elements a class name that starts with the name of the module, appended by an element name and separated by two underscores:

<nav class="nav">
  <a class="nav__item">

If we need to modify a module or its child elements, we add another class to the module or element that is comprised of its class name and a modifier separated by two dashes:

<nav class="nav nav--header">
  <a class="nav__item nav__item--header">

For state modifications, we add a modifier class prefixed with "is-".

<nav class="nav nav--header">
  <a class="nav__item nav__item--is-current">

Now, at a glance we can understand that these styles are constrained to the navbar module:

.nav {}  /* all navbars  */
.nav--header {} /* only header navs  */
.nav__item {} /* all nav-items */
.nav__item--header {} /* only header nav-items */
.nav__item--is-current {} /* all current nav-items */

Consistent Specificity Level

We keep the specificity of our style rules as low as possible across all of our CSS so that they're easy to override if we need to. To keep them low, we use classes instead of IDs or a combination of selector types, and we avoid using compound selectors.

Restrict Lengthy Selector Lists

It's not uncommon to use comma-separated selectors for elements that share the same style rule:

.contact-page,
.terms-of-service-page {
  border: 1px solid #000;
}

As a project grows, that same style rule might be reused again, so it's tempting to add another selector to the selector list:

.contact-page,
.terms-of-service-page,
.privacy-page {
  border: 1px solid #000;
}

Every time another selector is added to the list, it becomes a little more unwieldy and difficult to read. Since CSS classes are meant to be reused, it makes more sense to write a single class that can be easily applied in the markup.

We limit the number of selectors we use to two. If we find ourselves applying more selectors than that to a single rule, we know it’s a good time to consider creating another class:

.policy-page {
  border: 1px solid #000;
}

Extending CSS with Preprocessors &  JS Plugins

Using CSS preprocessors allows us to add features like variables, nesting, mixins, and functions to our development cycle for DRY-er and more organized code. For many projects, we’ve used either SASS or Stylus.

More recently we’ve used PostCSS in conjunction with React-based projects. PostCSS is a Node.js tool that transforms styles with JavaScript plugins. There are plugins for most of the features we used in SASS and Stylus (postcss-mixins, postcss-extend, and postcss-clearfix), and it's really fast and flexible.

One particularly useful plugin we use is cssnext, which allows us to use the latest CSS syntax without needing to wait for browser support. This has helped us declutter our code because we longer need to use vendor-prefixes when using a non-standardized feature.

Team Coding Standards

We’ve also documented some team coding standards that allow us to easily share our code with one another and keep our version control diffs clean:

  • Alphabetize property names.
  • Put all selectors on their own line.
  • If using rems, make sure they translate to whole number pixel values. If the root element is 16 px, then 1.06 rem translates to 16.96 px which isn't a real pixel value.
  • Place preprocessor functions at the top of the property list.

Writing transparent, modular CSS with good tools and sensible coding standards enables our team to work quickly and efficiently on a lot of exciting ideas. We hope some of these can be used or adapted to improve your own CSS workflow! If you have other ideas, send them to us on Twitter.