Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

Sass Style Guide

This is a loose guide for formatting Sass stylesheets to encourage consistency between developers in this repository. As with most things, there will be exceptions to the rules set forth here. When you come across those exceptions, please note them via comments.

Helper docs

Folder Structure

More info in each folder's readme...

Filename Purpose
base A collection of architecturally high-level styles for basic elements.
component Styles for components that make up our designs
layout Styles for page layout, how components layout on pages, etc.
libraries SASS resources, variables, mixins, etc
skin Styles for color schemes (if applicable to project)

When creating new .scss files

Not all files have to be partials, if a piece of CSS is uncommon, or part of the admin experience it's better to make it a separate file and include it as a library.

The build will aggregate partials into styles.css and compile non-partial files separately, in an identical folder structure under CSS.

Unfortunately, any time a new non-partial file is made you will need to restart the watch script so the build process can find it.

Basics

  • Use only soft tabs equalling two spaces. No hard TAB characters.
  • Remove trailing whitespace.
  • Do not define a unit for 0 values. (0px should be 0)
  • // comments will not be rendered on compile.

Indention & Spacing

  • Starting level selectors should have no indention.
  • Properties should be indented once (two spaces) under their selectors.
  • One space should follow a selector name before the opening declaration bracket.
  • Add one space between a property (after the colon) and value.

E.g.:

.selector {
  text-align: center;
  vertical-align: middle;
}

Sass Formatting

Selector Nesting

Please try to limit the nesting of selectors as much as possible. Nesting tends to turn into a spaghetti mess of code and specificity issues.

  • A newline should precede and follow a selector being nested.
  • Place nested selectors after the parent's properties/includes/extends/etc...

E.g.:

.selector {
  color: #ffffff;
  background: #000000;

  .nested-selector {
    text-align: center;
    border-radius: 5px;
    color: #cecece;
  }

}

Mixins, Nesting, and You

  • When using mixins that act as containers for style declarations (such as @include breakpoint()), never nest a selector within the mixins brackets.
  • @includes etc... should be placed (when sensible) at the end of a selector's properties. Specifically, add any breakpoint mixins in order of progressive enhancement.

E.g.:

.selector {
  color: #ffffff;
  background: #000000;
  @extend %selector-to-extend;
  @include element-invisible;
  @include breakpoint(lima) {
    color: #eeeeee;
  }
  @include breakpoint(papa) {
    color: #cccccc;
  }

  .nested-selector {
    // Nested selector content here.
    // Notice the newlines.
  }

}

See breakpoint names and values in core/_variables.scss.

Extending

While Sass' @extend functionality is pretty, it causes a lot of maintenance nightmares, so use it sparingly. The one absolute rule is:

Do not @extend a selector onto a selector in a different file.

In those cases it's usually best to use a mixin.

Class Naming Conventions

This project leverages a Component-Element-Modifier syntax for naming classes. This produces rather lengthy class names but also provides quick, easily discernible information to developers.

The syntax looks like this:

.component
.component__element
.component__element--modifier

The class names in this syntax do not imply any structural hierarchy in regard to markup. More often than not, the base component class is the containing element of the sub-components, but this is not always the case. You may end up with a markup structure in which an element is placed above the base component like so:

<div class="component__wrapper">
  <div class="component">
    <!-- other markup here -->
  </div>
</div>

There are two ways which modifier classes are usually applied. The first way is by creating the modifier class as an extension of the base class thereby ensuring that only one class is needed on a given element. This produces compiled CSS with many duplicate properties applied to classes and is not the preferred method.

.component__element {
  // styles
}

.component__element--modifier {
  @extend .component__element;
  // More styles
}

Instead, modifier classes should be applied in addition to base classes to achieve the desired result.

.component__element {
  // styles
}

.component__element--modifier {
  // separate or overriding styles
}
<div class="component">
  <div class="component__element component__element--modifier"></div>
</div>

Modular Scale Typography

We're using what's called modular scale for our typography. This means all of our type sizes are based on a ratio. We have a few helper functions.

You can use the mixin font-size() to declare the font-size you want, if you're looking at a mockup with font-sizes in px you can use Font Sizes Cheatsheet.

Here's an example:

.component {
  @include font-size(2); // From the cheatsheet we see 2 will result in 22.5px
}

Vertical Rhythm

We're also using vertical rhythm in this project, this means that all vertical spacing (margins, padding, top, bottom, etc) should be based on our vertical rhythm. Our vertical rhythm is a bit smaller on mobile, so it can be important to specify which breakpoint to use when getting vertical rhythm units.

To give you a rough idea of how large 1 vertical rhythm unit is:

1 vertical rhythm unit = the line-height of body text

We have a few helpers for this.

vr($breakpoint, $units)

This is the most basic

.component__flair {
  padding-top: vr(default, 1); // It assumes default breakpoint
  @include breakpoint(lima) {
    padding-top: vr(lima, 3);
  }
}

@include vr-all-breakpoints($properties, $units)

You'll probably use this the most. With this mixin you can pass one or a list of properties to set, and how many units to set it to.

.component__doo-dad {
  @include vr-all-breakpoints(margin-top, 2);
  @include vr-all-breakpoints(margin-bottom, 1);
  @include vr-all-breakpoints(padding-top padding-bottom, 0.5);
}

As you can see you can pass a fraction, ideally each component will start and end on a whole vertical rhythm unit. If something has a half of a unit at the top, try and add a half at the bottom to it evens out.

Line Height

Line height has to be specified on anything with a non-default font-size. To avoid bugs we made the line-height on body-unitless, but that means making sure it's specified on everything that's not @include font-size(1);

// If it can be the same vertical rhythm per breakpoint
.component__label {
  @include font-size(0); // 17.778px
  @include vr-all-breakpoints(line-height, 1); // 28 at default and 36 at lima
}

// If it needs to be different per breakpoint
.component__title {
  @include font-size(2);
  line-height: vr(default, 1);
  @include breakpoint(kilo) {
    @include font-size(7);
    line-height: vr(kilo, 1.5);
  }
}

Other Conventions

  • Do not add units to 0 values.

    // No.
    margin: 0px auto;
    // Yes.
    margin: 0 auto;
  • Feel free to add styles with multiple values across lines, unfortunately sass-lint doesn't have a rule for that, so you'll have to disable and re-enable the indentation rule.

    .component__button {
      // sass-lint:disable indentation
      transition:
      color 0.1s,
      background 0.25s,
      box-shadow 0.25s;
      // sass-lint:enable indentation
    }
    

CSS Property Declaration Order

This project follows Drupal's CSS declaration order:

.selector {
  // Positioning declarations
  position: absolute;
  top: 0;
  left: 0;
  z-index: 10;
  // Box model declarations
  display: inline-block;
  width: 100px;
  padding: 10px;
  border: 1px solid #333333;
  // Other declarations
  background: #000000;
  color: #ffffff;
  font-family: sans-serif;
  font-size: 10px;
}

More information can be found here: drupal.org

Declaring Color

When possible, use one of the predefined Sass variables in place for colors. Otherwise, when declaring hexadecimal color values, always use the full hex value. This makes it much easier to find and replace should we need to.

// No.
color: #333;
// Yes.
color: #333333;

Comments

Sectioning comments should look like so, with bottom borders extending to 80 characters:

// This is a section
// -----------------------------------------------------------------------------
.selector {
  color: $blue;
}

SassDocs

We're not currently using SassDocs, but we're using the commenting standards, see: Sass Doc Annotations

Documentation is only necessary on mixins, functions, and some variables (if they're not self evident).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.