Introduction
All CSS in a codebase should be consistent and give the impression it was written by a sole developer.
It should be understandable and simple to work with.
Syntax
Consistency is the most important factor when contributing to existing CSS. Generally the following
structure should be used:
- 4 space indentation (no tabs)
- 1 declaration per line
- A space after the colon separating the property and value pair
- Lowercase strings
- Always apply a semicolon to the last declaration
- Each selector should have it's own line
- Include a space before the opening brace of a declaration block
- Closing braces of a declaration block should be on a separate line
- Quote attribute selectors with single quotes
- Separate rules by new lines
{% highlight scss %}
// Do not format like this
.selector, .another-selector[attr=""]{
border:0; line-height:inherit;
width:auto}
{% endhighlight %}
{% highlight scss %}
// Format like this
.selector,
[attr=''] {
border: 0;
line-height: inherit;
width: auto;
}
{% endhighlight %}
###Strings
Prefer single quotes over double quotes. Once a standard has been set, stick with it.
{% highlight scss %}
.selector {
background-image: ('images/super-awesome-image.jpg');
font-family: 'Helvetica';
}
{% endhighlight %}
###Declaration Order
Alphabetical ordering is preferred. Although a very debatable subject, this style suits an unknown team size
because of the following:
- Consistency is the most important factor
- Everyone knows the alphabet
- Not all css contributors can distinguish between box-model properties, positioning, visual and typography
- No learning curve required for custom ordering preferences
{% highlight scss %}
// Define declarations alphabetically
.selector {
border: 1px solid rgba(0, 0, 0, .15);
border-radius: 3px;
box-shadow: inset 0px 1px 3px rgba(0, 0, 0, .1);
color: #fff;
width: auto;
}
{% endhighlight %}
###Units
Values of 0 should be unitless. Favour rem over px. Favour seconds over milliseconds. Use relative units whenever
possible such as on line-height. Avoid unnecessary leading zero's.
{% highlight scss %}
// Do not do this
.selector {
background-color: rgba(255, 255, 255, 0.4);
border: 0px;
font-size: 16px;
line-height: 24px;
}
{% endhighlight %}
{% highlight scss %}
// Use relative units
.selector {
background-color: rgba(255, 255, 255, .4);
border: 0;
font-size: 1rem;
line-height: 1.5;
}
{% endhighlight %}
If fallback values are required use a mixin when using Sass, otherwise add a fallback in a browser specific file.
###Colours
Colours should be written as lowercase hexademical values and in shorthand when possible. If transparency is required
favour rgba. Sass can parse hex values for rgba so colour variables and map values can always be stored
in hex.
{% highlight scss %}
// Do not do this
color: #FF6600;
{% endhighlight %}
{% highlight scss %}
// Do this
color: #f60;
{% endhighlight %}
###Shorthand
Group properties into shorthand use when appropriate to avoid adding multiple properties:
{% highlight scss %}
// Avoid this
.selector {
margin-bottom: 1rem;
margin-left: 2rem;
margin-right: 2rem;
margin-top: 1rem;
}
{% endhighlight %}
{% highlight scss %}
// Do this instead
.selector {
margin: 1rem 2rem;
}
{% endhighlight %}
Ensure that shorthand is not used repeatedly when it is not needed. If only one value needs changing
do not use shorthand:
{% highlight scss %}
// Don't do this
body {
font: normal 16px/1.5 'Helvetica';
}
.another-selector {
font: normal 2rem/1.5 'Helvetica';
}
{% endhighlight %}
{% highlight scss %}
// Do this
body {
font: normal 16px/1.5 'Helvetica';
}
.another-selector {
font-size: 2rem;
}
{% endhighlight %}
###Important rule
**Never use this**. Rework selectors instead. You will reap the benefits in the long term.
###Vendor Prefixes
Prefixes should **never be used in Sass files** and should be added via a build tool to ensure only
required prefixes are added. IE specific vendor prefixes should be added to an IE only file or
through the use of an IE mixin.
Selectors
###Naming
Class and variable names should be lowercase and hyphen separated rather than camelcase or snakecase.
{% highlight scss %}
// Do not do this
.formList {}
.form_list {}
{% endhighlight %}
{% highlight scss %}
// Use hyphenated lowercase
.form-list {}
{% endhighlight %}
###ID's
Never use ID's for selectors. If you are forced to reference an ID then use an attribute selector instead. This has the same specificity as a class
and so can be overwritten without increasing specificity levels.
{% highlight scss %}
// Never use ID selectors
#anidselector {}
{% endhighlight %}
{% highlight scss %}
// Prefer an ID attribute if it is absolutely necessary
[id='anidselector'] {}
{% endhighlight %}
###Qualifying
Never tag-qualify. By qualifying selectors specificity becomes a problem. Always rework your markup and class structure to address inheritance issues
instead of qualifying.
{% highlight scss %}
// Instead of this
input[type='checkbox'] {}
{% endhighlight %}
{% highlight scss %}
// Use this
[type='checkbox'] {}
{% endhighlight %}
###JS-Classes
Any classname prefixed with js- should not have any presentational properties attached. These are used as references for JS Classes.
Instead is- prefixed classes should be used as state classes.
{% highlight scss %}
// Don't do this
.js-navigation {
display: block;
}
{% endhighlight %}
{% highlight scss %}
// Use this
.is-active {
&.navigation {
display: block;
}
}
{% endhighlight %}
This allows for the removal of JS without interfering with presentation.
Box Model
A document should use 1 box-model type consistently. Once a box-sizing value has been set there should not
be any other elements using an alternate value.
{% highlight scss %}
// Do not do this
html,
body {
box-sizing: border-box;
}
.element {
box-sizing: content-box;
}
{% endhighlight %}
###Float Containment
Clearing should be performed through a pseudo-class clearfix. *Overflow clearing* should not be used.
{% highlight scss %}
// Don't do this
.container {
overflow: auto;
}
.child-element,
.child-element-2 {
float: left;
width: 50%;
}
{% endhighlight %}
{% highlight scss %}
// Do this
.container {
&:after {
clear: both;
content: '';
display: table;
}
}
.child-element,
.child-element-2 {
float: left;
width: 50%;
}
{% endhighlight %}
Inheritance
Inheritance should be used whenever possible to avoid declaration duplications.
{% highlight scss %}
// Instead of duplicate declarations
.promotion-banner p,
.promotion-banner h1 {
font-size: 1.2rem;
}
{% endhighlight %}
{% highlight scss %}
// Use inheritance
.promotion-banner {
font-size: 1.2rem;
}
{% endhighlight %}
Avoid redeclaring values when they aren't necessary:
{% highlight scss %}
// Don't do this
p {
color: #666;
margin: 1rem 0;
}
.parent {
color: #000;
}
.parent p {
color: #000;
margin: 1rem 0 2rem;
}
{% endhighlight %}
{% highlight scss %}
// Use inheritance when possible
p {
color: #666;
margin: 1rem 0;
}
.parent {
color: #000;
}
.parent p {
color: inherit;
margin-bottom: 2rem;
}
{% endhighlight %}
Comments
Comments should be added at the top of each component to indicate the purpose and use of the component. They should also be added
above a selector when appropriate to explain references to other elements/classes/components. These should use the // syntax. e.g.
{% highlight scss %}
//
// If a form-horizontal parent modifier class is assigned,
// the form-list component changes to a horizontal layout
// for viewports larger than small screens
//
.form-horizontal & {
@include breakpoint(map-get($breakpoints, 'small')) {
}
}
{% endhighlight %}
Sass mixins and functions should also be marked with annotations providing a description, any arguments and dependencies.
Sassdocs annotations are preferred.
For a Sass projects main file a table of contents should be added at the top of the file for reference of all partials
within the project. This should be updated whenever a partial is added e.g.
{% highlight scss %}
//
// CONTENTS
//
// UTILITIES
// Functions............Useful functions.
// Mixins
// Breakpoint.......Media query mixin.
// Rem..............Conditionally output px or rem.
// Before IE 10.....Conditionally output content for pre IE10.
// Before IE 9......Conditionally output content for pre IE9.
// Bourbon..........Bourbon Mixin library - See http://bourbon.io/docs/
{% endhighlight %}
Sass
###Imports
**Never use inline imports**. For imports inside .scss files do not use underscores or filename extensions.
{% highlight scss %}
// Don't do this
@import '_form-list.scss';
{% endhighlight %}
{% highlight scss %}
// Do this
@import 'form-list';
{% endhighlight %}
###Values
Reusable values should be stored in variables without a unit attached.
{% highlight scss %}
// Don't do this
$vertical-spacing: 20px;
{% endhighlight %}
{% highlight scss %}
// Do this
$vertical-spacing: 20;
{% endhighlight %}
###Calculations
When performing Math in Sass always enclose them in parentheses.
{% highlight scss %}
// Don't do this
width: $container-width / 2;
{% endhighlight %}
{% highlight scss %}
// Do this
width: ($container-width / 2);
{% endhighlight %}
###Maps
Maps should be used whenever values will be reused repeatedly. Common examples include breakpoints, colour groups and z-index values.
Maps should have the following syntax:
- Hyphenated name
- Single quoted keys
- Space after the colon
- Opening brace on same line as map name
- Key/Value pairs on same line
- Comma at end of each key/value pair except the last
- Closing brace on it's own line
{% highlight scss %}
$breakpoints: (
'small': 30rem,
'medium': 48.125rem,
'large': 75rem
);
{% endhighlight %}
These can then be fetched through the map-get function:
{% highlight scss %}
map-get($breakpoints, 'small')
{% endhighlight %}
###Variable Naming
Names should not be based on buzzwords or physical objects. They should be developer friendly and understandable.
Colour names should be representative of themselves. If colour names are difficult to remember, additional variables
can be created that reference the original colours.
{% highlight scss %}
// Do this
$brand-colours: (
'teal': #088,
'royal-blue': #4169E1
);
$divider-colour = map-get($brand-colours, 'teal');
{% endhighlight %}
###Nesting
Selector nesting should be kept as simplistic as possible. Attempting to be clever (although impressive) will make sass
harder to read and take longer to analyze. Nesting should also be kept to a maximum of 3 levels deep. If a selector needs to be
more than 3 levels deep then refactoring is required. There is always a way to reduce the specificity and still target
the desired elements.
{% highlight scss %}
// Don't do this
.navigation {
ul {
>li {
&:first-child {
margin-left: 0;
}
}
}
}
{% endhighlight %}
{% highlight scss %}
// Be terse
.navigation-parent-item {
&:first-child {
margin-left: 0;
}
}
{% endhighlight %}
###Animations, Mixins & Functions
Each mixin, function and animation should be separated out into it's own partial and documented using SassDoc annotations.
###Extend
@extend should be reserved for use on sass placeholders only.
###Sass Declarations
In sass rulesets declarations should be in the following order:
- Extends
- Includes without content
- Normal declarations
- Includes with content
- Nested rule sets
###Media Queries
Use an existing mixin for declaring a media query and place it nested inside the ruleset. This allows the developer
to easily find related media queries. Optimisation for duplicate media queries can be dealt with as a performance task.
{% highlight scss %}
.selector {
@include linear-gradient(to bottom, #fff, #f2f2f2, $fallback: #fff);
@include breakpoint(map-get($breakpoints, 'medium')) {
background-color: inherit;
background-image: none;
}
}
{% endhighlight %}
Legacy Browsers
Sass allows handling of legacy browser specific css without the use of hacks. Hacks should never be used under
any circumstance. For old IE browser versions, a separate css file should be created with fallbacks. This
ensures that modern browsers are not populated with unnecessary declarations. In Sass projects use the
IE mixin:
{% highlight scss %}
.selector {
background-color: rgba(255, 255, 255, .6);
@include before-ie-9 {
background-image: url('images/white-transparency-60.png');
}
}
{% endhighlight %}