This is the Up&Up CSS / Sass styleguide that I wrote up for our team. I started work on it in the fall of 2016 and we have been rolling it out to all new projects. After writing CSS / SCSS / Sass for a few years now, I really enjoyed sitting down and reflecting on how I have been writing it, how the team has been writing it, and how we could work better together by being intentional about how we write our code.

I’m publishing it here for the purpose of showing how a team develop their own code style guide that works for them and to show the thought process behind it. A lot of the thoughts behind this were a result of a whole-site CSS refactor for one of our clients.

Purpose

The purpose of this document will be to define some standardized best practices for how our CSS is composed and organized in our projects.

Principles

The Goals of Drupal 8’s CSS philosophy will serve as a good starting point for our CSS philosophy. Well-architected CSS should be:

1. Predictable

CSS throughout Drupal core and contributed modules should be consistent and understandable. Changes should do what you would expect without side-effects.

2. Reusable

CSS rules should be abstract and decoupled enough that you can build new components quickly from existing parts without having to recode patterns and problems you’ve already solved. – Philip Walton, CSS Architecture

3. Maintainable

As new components and features are needed, it should be easy to add, modify and extend CSS without breaking (or refactoring) existing styles.

4. Scalable

CSS should be easy to manage for a single developer or for large, distributed teams (like Drupal’s).

Source: CSS architecture (for Drupal 8)

In addition, we should keep Mark Otto’s Golden Rule in mind:

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

Code Guide by Mark Otto

Style Guide

High level (borrowed from css guidelines):

  • Sass, not scss.
  • Four (4) space indents, no tabs.
  • Multi-line css
  • Meaningful use of white space.

Sass

Moving forward, we will depart from our Scss convention and start using Sass. This has the benefit of less brackets {} and semi-colons and overall a cleaner feel.

Each project should include Sass linting. Use our default .sass-lint.yml file. It will point out the rules below for you.

Four space indents

Since we aren’t using brackets, this keeps the Sass clearer and also is aligned with our Javascript and PHP standards.

Multi-line css

Selectors and properties each get their own line. Do it like this:

.container
    width: 95%
    max-width: 40em

Meaningful use of white space

White space can give context to class definitions. Include two empty lines between top-level css blocks, and one line between properties and nested classes.

Example:

.card
    padding: 1rem
    margin: 1rem

    &__header // leave one empty line above this since it is nested
        border-bottom: 1px solid gray

.card--white // two empty lines above this new class block
    background-color: white

Nesting Depth

Nesting depth should be limited to 2.

Example:

.block
    &__element
        &--modifier // Deepest Nest Allowed

Limit line length to 80 characters

This helps make the code more readable without horizontal scrolling. Because you’re writing multi-line css, this should really only come into play for comments. Limiting your line length will make your comments easier to read and therefore more useful. For instances like long urls or gradient syntax, don’t worry about it.

Example:

    // This file is where you override default Bootstrap variables. You
    // can find a list of the default Bootstrap variables
    // in _variables.scss

Use single line comments

Your comments don’t have to be on one single line, just don’t use /* These kinds of comments */, because they will end up in the compiled css. Your comments, even if they are multi-line, should look like this:

    // This file is where you override default Bootstrap variables. You
    // can find a list of the default Bootstrap variables
    // in _variables.scss

Clean Import paths

You don’t need to include leading underscores or filename extensions in your import paths. To stay consistent, your imports should look like this:

    @import "base/typography" // where this file is base/_typography.sass
    @import "base/colors"

    @import "layout/grid"
    @import "layout/containers"

Class Name Format

Class names should use full words rather than abbreviations. Remember that your class names are written for the benefit of other developers, not the computer. Prefer class="button" to class="btn".

Class names for components should use dashes between words. Prefer class="component-name" to class="componentname".

We will use a BEM-style class naming system. BEM stands for Block Element Modifier. You can also think about it as Component, Sub-object, Variant.

Examples:

    .block
        .block--modifier
        .block__element
        .block__element--modifier

    .component-name
        .component-name--variant
        .component-name__sub-object
        .component-name__sub-object--variant

    .component-name
        &--variant
        &__sub-object
        &__sub-object--variant

    // A real world example from http://github.com/mergeweb/v2heavy
    .site-header // the site header
        .site-header__top // the top portion of the site header
        .site-header__bottom // the bottom portion of the site header
        .site-header--admin // variant of site-header

Additionally, limit your BEM depth to 1. This means that blocks should not have nested elements. If there are elements nested inside of elements, start a new block.

    // Don't do this
    .block__element__sub-element

    // Definitely don't do this
    .block__element
        &__sub-element
            &__sub-element-2

Consider name-spacing layout and javascript specific classes.

For instance, instead of using a class name like .container, you could use .layout-container. Or .layout-grid, over .grid. This gives the benefit of clear separation between component

Or for javascript that is manipulating the DOM, use something like .js-behavior-hook instead of .behavior-hook to make sure that it is a dedicated class not used for styling.

Source: Drupal 8 CSS Architecture | Class Name Formatting_

Avoid Using id Selectors

You can use them for Javascript or for providing anchor-links, but don’t use them for styling.

Avoid Vendor Prefixes

For sites that are using a build tool like Grunt or Gulp, we can skip vendor prefixes in the css in favor of using the PostCSS Autoprefixer plugin. It will automatically determine what prefixes are necessary based on browser support requirements and only include the necessary

Use relative units for font-sizing.

Prefer relative units like rem or em for font-sizing. This allows for more flexible, more maintainable font styling. It also allows us to better control font styles based on what fonts have been loaded or not loaded.

Likewise, avoid specifying units for line-height. Line-height should be a ratio of font-size. You will need to write a lot less css if your line-height ratios are on to begin with.

Linting

The easiest way to put these guidelines into practice will be to use sass-lint. You can install it globally like so: npm install -g sass-lint or on a per-project basis like this: npm install sass-lint --save-dev

Add a copy of our .sass-lint.yml to the root of your project.

To lint your project from the command line, you can do this:

sass-lint -v

Or, for as-you-go linting, install the sass-lint plugin for your respective editor:

sass-lint documentation

Resources

We leaned heavily on the following resources for these guidelines:


Have any comments or questions about this post? Send them to me via email hi@benrobertson.io or on Twitter @banquos_ghost.