While many people think of Sass as just CSS there are many unique challenges that need to be addressed if we want to be able to write the best possible code. In this document I'll go through some coding styles that will help you share code with others and avoid a common pitfalls.
In addition to many of these principles you should read Idiomatic CSS and apply standard CSS best-practices along with these. Understanding how to write great CSS is the foundation this is built upon. For this reason, there will be a bit of overlap.
Writing good Sass code starts with correctly dividing and modularizing your objects. It is arguably more important than any other aspect of writing CSS.
Well-written Sass is:
Objects should never manipulate other objects. eg. .message
would never change the style of a nested object
called .list
. Instead use child selectors like .message__list
and use both classes in the markup
<div class="list message__list">
or use a modifier <div class="message"><div class="list list--small"></div></div>
Break functionality into smaller objects. Each object should do one thing and do it well.
Never, ever use location-based styling. This means a block is never styled different because it is within another block. Objects should have "modifiers" instead of location-related styles .block--large {}
instead of #sidebar .block {}
Yep, never. You don't need them and they aren't re-usable by nature
This means an object that handles background and border won't control padding and margin. Styles generally fall into a couple of categories: layout, texture, typography. Each object should generally only handle one of these. But be pragmatic about it and consider reusability at all times.
Enforce these rules by using one of the naming conventions in the next section.
It's important that you use a consistent naming convention for your selectors. For this, I recommend looking at the BEM or Montage.
.block-name {}
.block-name__child-name {}
.block-name--modifier {}
.namespace-BlockName {}
.namespace-BlockName-childName {}
.namespace-BlockName--modifier {}
The most important thing is that you pick one as a team and stick with it.
$variable
should always appear at the top. @extend
should always appear before properties. It's like extending a class in Ruby.@include
should appear second. This allows the properties to override the mixins.@include someMixin { properties }
&.modifier
The basic rule of thumb is at-rules, properties, then blocks.
Here is an example of a well-formed selector:
.selector-1,
.selector-2,
.selector-3[type="text"] {
$bg: blue;
$fallback: green;
@extend .clearfix;
@include border-box;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
display: block;
font-family: helvetica, arial, sans-serif;
color: #333;
background: #fff;
background: linear-gradient(#fff, rgba(0, 0, 0, 0.8));
@include after {
position: absolute;
}
&.selector--modifier {
background: red;
}
.selector__child {
display: none;
}
}
block-list
object will live in a block-list.scss
file.-rg-columns
columns
@extend
@mixin GridBuilder
@mixin borderBox
@mixin as-GridBuilder
@mixin -gridHelper
// Loop through each breakpoint and build
// classes for each using the breakpoint mixins
// First breakpoint is no media query — mobile-first.
//
// @param {List} $breakpoints List of column breakpoints
// @param {Boolean} $spacing Include spacing classes?
// @param {Boolean} $visibility Include visibilty classes?
// @param {Boolean} $layout Include layout classes?
// @api private
@mixin -rg-Breakpoints($breakpoints, $spacing: true, $visibility: true, $layout: true) {
@each $columns in $breakpoints {
@if index($breakpoints, $columns) == 1 {
@include -rg-BreakpointClasses($columns, $spacing, $visibility, $layout);
}
@else {
@include rg-from($columns) {
@include -rg-BreakpointClasses($columns, $spacing, $visibility, $layout);
}
}
}
}
Sharing Sass code is becoming more important. Without the use of a proper module system in Sass we need to establish a few rules so that sharing code is consistent and behaviour is predictable. Sass packages are popping up in Bower and Github but there is no consistency in the way they are implemented.
A few general rules:
@mixin -rg-gridUnit
@import "rg-Grid/mixins"
components
directory.local
directory adjacent to the components
directory.assets
directoryExample structure:
/module-name
/assets
/fonts
/images
/components
/responsive-grid
/clearfix
/animation
/local
/homepage
/lib
/mixins
/functions
bower.json
index.scss
rg-Grid
index.scss
file as the entry point: @import "module-name/index"
. rg-Grid
module would have a rg-Grid
mixinuser/project
eg. fonzie/responsive-grid
components
directorycomponents
directory is assumed.As every module does not output anything just by being imported, packages can safely import other packages without the fear that a file has already been imported. Because of this, dependencies can safely require their own dependencies.
For example, a Grid
package depends on a Clearfix
package, but so does the LayoutHelpers
package. Both
of these packages can @import "clearfix/index"
without fear that there will be two .clearfix
classes in the output.
It is assumed that the components
directory is added as a load path, so packages can easily require their dependencies.
A single piece of the design, usually fairly small. This could be things like .message
, .block
, .list
, .post
.
Objects should be independent. Think of them as lego blocks. Objects have "modifiers" and "children".
If an "object" is the parent, any sub-parts of that object are considered its children. Children are only ever
controlled by the object it belongs to. A .message
object may have a title that it styles, .message__title
,
this would be referred to as a child element.
A single piece of functionality that can be composed of CSS, mixins, functions and assets (such as images or fonts). A module has a single entry point and a single purpose and role. eg. A grid framework could be a module.
When a module is shared with others via a package manager like Bower it will generally be referred to as a package. This means that the term "module" and "packages" are fairly interchangable.
This is another term for the concept of an "object".
When referring to "objects" and "blocks", the word "element" is interchangable with the word "children".
"Objects" may be modified in a way that changes their style in small ways, think of them as themes or alternative
styles. For example, a .list
object may have a .list--small
modifier to make the text smaller.