Skip to content

Instantly share code, notes, and snippets.

@jedfoster
Last active July 8, 2016 19:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jedfoster/00201a65fc6bb167b093 to your computer and use it in GitHub Desktop.
Save jedfoster/00201a65fc6bb167b093 to your computer and use it in GitHub Desktop.
Coding Style Guide for Sass

Coding style

(Largely cribbed from GitHub's Styleguide)

Spacing

  • Use soft-tabs with a two space indent. Spaces are the only way to guarantee code renders the same in any person's environment.
  • Put spaces after : in property declarations.
  • Put line breaks between rulesets.
  • When grouping selectors, keep individual selectors to a single line.
  • Each declaration should appear on its own line for more accurate error reporting.

Formatting

  • Use // for comment blocks (instead of /* */).
  • Avoid specifying units for zero values, e.g., margin: 0; instead of margin: 0px;.

Sass

  • Use the white-space, Sass syntax, not the CSS-superset SCSS syntax
  • Whenever possible, use a variable for colors and font-sizes, rather than literal values
  • Declare any @extend something-else or +mixin at the top of your rules, followed by properties and nested selectors, separated by an empty line
// Good
.DishHero
  @extend %dish-media-object
  @include border-radius

  padding-bottom: $padding-s
  margin-bottom: $padding-m
  border-bottom: 1px solid $border-color

  // psuedo-classes of the current selector should be declared _before_ child selectors
  &:hover
    background: $highlight

  img
    @extend %media-object

    // more properties
    // repeat as needed


// Bad
.DishHero
  padding-bottom: $padding-s
  margin-bottom: $padding-m
  @extend %dish-media-object
  border-bottom: 1px solid $border-color
  @include border-radius
  img
    // more properties
    @extend %media-object

Nesting

Observe the "Inception Rule": Don’t go more than four levels deep. Try to keep it to three levels, reserving the fourth level for pseudo-classes (i.e. :hover or :active)

// Bad
.reality
  .van-chase
    .hotel
      .snow-fortress
        // this is a stretch

        .limbo
          // don't do this
// Good
.ChefsFeed
  .title
    a
      color: $black

      &:hover
        color: $blue

Media queries

Media queries should be nested inside the selector they modify, not outside.

(Sass cannot extend a selector that is nested under a media query. This mainly affects only % placeholder selectors, but we should be consistent.)

// Good
.restaurant_photos-multi_edit
  .user-content-wrapper
    @include screen-md
      display: flex
      flex-wrap: wrap

// Bad
.restaurant_photos-multi_edit
  @include screen-md
    .user-content-wrapper
      display: flex
      flex-wrap: wrap

Take media queries into account when observing the Inception Rule; media queries nest, and should therefore be treated as a new level.

// Bad
.reality
  .van-chase
    // styles

    @media $hotel
      .snow-fortress
        // this is a stretch

        .limbo
          // don't do this
// Good
.ChefsFeed
  .title
    a
      color: $black
      
      &:hover
        color: $blue

      @media $small-screen
        // styles are OK here, but don't nest anymore selectors

File organization

stylesheets/responsive
├── abtests
├── addons
├── base
├── ie_sass
├── layouts
├── modules
├── patterns
├── vendor
├── _config.sass
├── _sketch-pad.sass
└── application_responsive.sass

With the exception of addons each directory contains a file named _index.sass which serves as a manifest for that directory; all files in the directory should be @import'ed in _index.sass.

  • abtests contains styles for currently running A/B tests
  • addons contains styles for third-party widgets and plugins. All addons files that are compiled to stand-alone CSS files
  • base contains the basic, elemental styling
  • ie_sass contains styles for IE
  • layouts, modules, patterns contain styles for the respective layers (see Larger principles below)
  • vendor contains a manifest of third-party styles that required by our application styles (i.e. Bootstrap)

File naming

All filenames should begin with an underscore (_).

Files should be named after their top-level selectors. If your top-level selector is .my-awesome-selector, your file should be named _my-awesome-selector.sass

Sub-directories

Within modules and patterns you may find yourself with a need for more than a single file. For example, the block-grid pattern is more complex, so the Sass for that pattern is broken into separate files in a sub-directory of patterns/

stylesheets/responsive
├── patterns
|   ├── block-grid
|   |   ├── _functions.sass
|   |   ├── _index.sass
|   |   ├── _mixins.sass
|   |   └── _placeholders.sass

Again, _index.sass serves as a manifest of the files composing this pattern.

Within modules, sub-directories can server to group concerns. For example, the modules/dish directory contains a number of files all relating to the styling of dishes.

What goes in a file?

Module and layout files should consist of only one top-level selector.

// Good:

// modules/_dimmi_reservation.sass
.DimmiReservation
  .form-group
    @extend %col-sm-3
    margin-bottom: $padding-x

    @include screen-sm
      margin-bottom: 0

  .make-reservation-btn
    @extend %btn
    @extend %btn-primary
    @extend %btn-block

    margin: 0 $padding-sm
    width: auto

  .form-control
    margin-bottom: 0


// Bad:

// modules/menu/_menu_photos.sass
.MenuPhotos-primary
  ul
    @include block-grid(4)

.AddMenuPhoto
  background: image-url('1/menu-background-blur.jpg') no-repeat 50% 50%
  line-height: 1.1

  +screen-xs
    font-size: 20px

  &:hover
    text-decoration: none
    color: white

  &:after
    content: ""
    left: 50%
    margin-left: -150px

(An exception to this rule would be if your top-level selector is a placeholder, which you then immediately @extend, as in modules/dish/_dish_grid.sass. But this is relatively rare in modules and non-existent in layouts.)

Base and pattern files can contain multiple top-level selectors. Top-level selectors in pattern files should always be placeholders—patterns should never output literal CSS.

Base top-level selectors should be either HTML elements (p, a, table, etc.) or placeholders, they should not be classes or IDs.

(Our current code base does not meet these standards, but that should not stop us from implementing these standards on new code.)

Selector naming

Where possible, avoid IDs as selectors, whether at the top level or nested. There will be exceptions to this rule, of course, but if you choose an ID as your selector, have a good reason.

When naming a selector favor clarity over brevity; .primary-button is better than .btn--p.

Larger principles

The problem

Bootstrap is based on OOCSS principles.

Object-oriented CSS tries to "solve" CSS by emulating the idea of OOP. It does this by breaking styles down to the smallest possible parts and putting them in separate classes. So we end up with class chains like this in our markup:

<button class="btn btn-primary btn-green btn-large">Save</button>

That's a lot of classes that don't really describe our button.

A solution

Semantic naming

<button class="save">Save</button>

How do we get there?

Principles of a semantic, modular CSS architecture:

Elements

Visual elements are the basic building blocks of the UI. Well engineered individual elements reduce duplication and increase consistency.

Elements include: typography, colors, buttons, grid, etc.

Sass for elements that map directly to HTML elements (hN, p, header, etc.) would output actual CSS, while Sass for things like buttons or the grid should be implemented as placeholders.

Patterns

Patterns are small abstract units of frequently used styles.

Patterns include: an input with a label, field set presentation, and other commonly occurring styles.

Patterns should always be implemented as placeholders, optionally backed by a mixin.

Modules

Modules are a "semantic" and specific assemblage of one or more elements, patterns, or even other modules.

Modules include: page header, navigation, hero

The line between "pattern" and "module" can be blurry and subject to personal interoperation, but I like to think of a module as anything that appears only once on a page (like the page header or footer) and patterns as anything that appears in multiple places.

In practice, when engineering modules, from one to the next, small UI patterns may emerge. If practical, try to encapsulate these smaller patterns for reuse, but don't lose sleep over them.

Elements, patterns, and modules should never assume a particular outer size; they should be engineered to occupy 100% of their containers. Elements within a module can have width in relation to sibling elements, but the module itself should not.

Layouts

Layouts control sizing and page-specific styles. I like to think of the layout file as the mortar between the bricks of my modules and patterns.

The layout Sass file defines the overall width of the page as well as the widths and relationships of the modules and patterns on the page.

In general a module or pattern should not contain any width styling. This makes the module more portable. A module should be self-contained and not be concerned with where or how it will be used. This allows for flexibility when it comes time to assemble modules/patterns for a given page.

Layouts are an appropriate place to put style overrides for modules that are shared across pages.

Further reading

  1. Clean out your Sass junk-drawer
  2. Sass in the Real World
  3. Toadstool
@jedfoster
Copy link
Author

Test comment

  • point 1
  • point 2
  • point 3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment