Pixel Union Code Guide

Standards for developing flexible, durable, and sustainable HTML and CSS.

Lovingly forked from @mdo

The Golden rule

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

Table of contents

HTML

Syntax

<!DOCTYPE html>
<html>
  <head>
    <title>Page title</title>
  </head>
  <body>
    <img src="images/company-logo.png" alt="Company">
    <h1 class="hello-world">Hello, world!</h1>
  </body>
</html>

HTML5 doctype

Enforce standards mode and more consistent rendering in every browser possible with this simple doctype at the beginning of every HTML page.

<!DOCTYPE html>
<html>
  <head>
  </head>
</html>

Language attribute

From the HTML5 spec:

Authors are encouraged to specify a lang attribute on the root html element, giving the document's language. This aids speech synthesis tools to determine what pronunciations to use, translation tools to determine what rules to use, and so forth.

Read more about the lang attribute in the spec.

Head to Sitepoint for a list of language codes.

<html lang="en-us">
  <!-- ... -->
</html>

IE compatibility mode

Internet Explorer supports the use of a document compatibility <meta> tag to specify what version of IE the page should be rendered as. Unless circumstances require otherwise, it's most useful to instruct IE to use the latest supported mode with edge mode.

For more information, read this awesome Stack Overflow article.

<meta http-equiv="X-UA-Compatible" content="IE=Edge">

Character encoding

Quickly and easily ensure proper rendering of your content by declaring an explicit character encoding. When doing so, you may avoid using character entities in your HTML, provided their encoding matches that of the document (generally UTF-8).

<head>
  <meta charset="UTF-8">
</head>

CSS and JavaScript includes

Per HTML5 spec, typically there is no need to specify a type when including CSS and JavaScript files as text/css and text/javascript are their respective defaults.

HTML5 spec links

<!-- External CSS -->
<link rel="stylesheet" href="code-guide.css">

<!-- In-document CSS -->
<style>
  /* ... */
</style>

<!-- JavaScript -->
<script src="code-guide.js"></script>

Practicality over purity

Strive to maintain HTML standards and semantics, but not at the expense of practicality. Use the least amount of markup with the fewest intricacies whenever possible.

Attribute order

HTML attributes should come in this particular order for easier reading of code.

Classes make for great reusable components, so they come first. Ids are more specific and should be used sparingly (e.g., for in-page bookmarks), so they come second.

<a class="..." id="..." data-modal="toggle" href="#">
  Example link
</a>

<input class="form-control" type="text">

<img src="..." alt="...">

Boolean attributes

A boolean attribute is one that needs no declared value. XHTML required you to declare a value, but HTML5 has no such requirement.

For further reading, consult the WhatWG section on boolean attributes:

The presence of a boolean attribute on an element represents the true value, and the absence of the attribute represents the false value.

If you must include the attribute's value, and you don't need to, follow this WhatWG guideline:

If the attribute is present, its value must either be the empty string or [...] the attribute's canonical name, with no leading or trailing whitespace.

In short, don't add a value.

<input type="text" disabled>

<input type="checkbox" value="1" checked>

<select>
  <option value="1" selected>1</option>
</select>

Reducing markup

Whenever possible, avoid superfluous parent elements when writing HTML. Many times this requires iteration and refactoring, but produces less HTML. Take the following example:

<!-- Not so great -->
<span class="avatar">
  <img src="...">
</span>

<!-- Better -->
<img class="avatar" src="...">

JavaScript generated markup

Writing markup in a JavaScript file makes the content harder to find, harder to edit, and less performant. Avoid it whenever possible.

Sass

Syntax

Questions on the terms used here? See the syntax section of the Cascading Style Sheets article on Wikipedia.

/* Bad CSS */
.selector, .selector-secondary, .selector[type=text] {
  padding:15px;
  margin:0px 0px 15px;
  background-color:rgba(0, 0, 0, .5);
  box-shadow:0px 1px 2px #CCC,inset 0 1px 0 #FFFFFF
}

/* Good CSS */
.selector,
.selector-secondary,
.selector[type="text"] {
  padding: 15px;
  margin-bottom: 15px;
  background-color: rgba(0,0,0,0.5);
  box-shadow: 0 1px 2px #ccc, inset 0 1px 0 #fff;
}

.selector {
  width:100px; /* No */
  width: 100px; /* Yes */
}

Selectors

Additional reading:

/* Bad */
#tasks-wrapper .tasks > div input[type="text"] { ... }
.task-item .label { ... }

/* Good */
.task-item-edit-title { ... }
.task-item-label { ... }

Nesting

Nesting is very helpful to define element states such as active and hover, pseudo elements like before and after, and minimal child selectors that don't justify an additional class name.

// Bad
.tasks-items {
  // styles

  > div {
    // styles

    .selector {

      label {
        // styles

        &:hover {
          // styles
        }
      }
    }
  }
}

// Good
.task-items {
  // styles
}

.task-item {
  // styles
}

.task-item-label {
  // styles

  &:hover {
    // styles
  }
}

Variables

Variables in Sass are a powerful way to define values in one place that can be reused in multiple places in your project. They allow you to make changes from a central point without needing to use find and replace across multiple files and directories. [ref]

// Variable names should describe the purpose or function

$red: red; // Bad
$yellow: yellow; // Bad

$brand-color: red; // Good
$accent-color: yellow; // Good

// Use hyphens to separate multiple words

$badVariable: red; // Bad
$bad_variable: red; // Bad
$good-variable: red; // Good

@include vs @extend

First, some basics:

When to use @extend

This should be your first plan of attack. Using an @extend limits customizability but produces a more efficient CSS file. Smaller is better.

When to use @include

If you need to overwrite declarations of an @extend or need to pass values to it then you'll want to set up a mixin and use @include instead. Mixins are powerful and are generally used to accomplish complicated or labor-intensive tasks. Some examples:

/* Extend a class */
.error {
  color: red;

  &:hover {
    color: darken(red, 5%);
  }
}

.my-element {
  @extend .error;
}

/* Create a mixin and include it somewhere else */
@mixin icon-font($font-size: 16px) {
  font-family: "Icon Font";
  font-weight: normal;
  font-size: $font-size;
}

.icon {
  @include icon-font();
}

.large-icon {
  @include icon-font(20px);
}

Comments

Code is written and maintained by people. Ensure your code is descriptive, well commented, and approachable by others. Great code comments convey context or purpose. Do not simply reiterate a component or class name.

Be sure to write in complete sentences for larger comments and succinct phrases for general notes.

/* Bad example */
/* Modal header */
.modal-header {
  ...
}

/* Good example */
/* Wrapping element for .modal-title and .modal-close */
.modal-header {
  ...
}

Media query placement

Place media queries inside their respective selector. Don't have entire blocks of code dedicated to a single media query.

Feel free to create a media query mixin to simplify modifying your queries.

// Bad example

.element {
  // styles
}

.element-header {
  // styles
}

@media (min-width: 480px) {
  .element {
    // styles
  }

  .element-header {
    // styles
  }
}

// Good example

.element {
  // styles

  @media (min-width: 480px) {
    // styles
  }
}

.element-header {
  // styles

  @media (min-width: 480px) {
    // styles
  }
}

// Example mixin

@mixin breakpoint($point) {
  @if $point == desktop {
    @media (min-width: 960px) { @content; }
  }
  @else if $point == tablet {
    @media (min-width: 720px) { @content; }
  }
  @else if $point == mobile {
    @media (min-width: 320px)  { @content; }
  }
}

.element {
  // styles

  @include breakpoint(mobile) {
    // styles
  }
}

Single declarations

Even in cases where you only have one rule per selector, avoid single-line formatting. In the same way that consistent trailing commas in Javascript makes for cleaner diffs, so does maintaining consistent formatting across all CSS rules, regardless of the number.

/* Multiple declarations, one per line */
.sprite {
  display: inline-block;
  width: 16px;
  height: 15px;
  background-image: url(../img/sprite.png);
}

/* Single declarations, one per line too! */
.icon {
  background-position: 0 0;
}

Shorthand notation

Strive to limit use of shorthand declarations to instances where you must explicitly set all the available values. Common overused shorthand properties include:

Often times we don't need to set all the values a shorthand property represents. For example, HTML headings only set top and bottom margin, so when necessary, only override those two values. Excessive use of shorthand properties often leads to sloppier code with unnecessary overrides and unintended side effects.

The Mozilla Developer Network has a great article on shorthand properties for those unfamiliar with notation and behavior.

/* Bad example */
.element {
  margin: 0 0 10px;
  background: red;
  background: url("image.jpg");
  border-radius: 3px 3px 0 0;
}

/* Good example */
.element {
  margin-bottom: 10px;
  background-color: red;
  background-image: url("image.jpg");
  border-top-left-radius: 3px;
  border-top-right-radius: 3px;
}

Class names

/* Bad example */
.t { ... }
.red { ... }
.header { ... }

/* Good example */
.tweet { ... }
.important { ... }
.tweet-header { ... }

Organization

Thanks to compilation and compression, we're able to organize styles into many small files without impacting the end user. Rules should be organized into small sets corresponding to the elements they style. Styles of unrelated elements should never be in the same file.

Maintainability

Consider the example on the right.

This rule obviously hides something, but it's not clear what it's hiding, or why. This code is unmaintainable, because its purpose is not obvious. Strive to write selectors and styles whose purpose and relationship is intuitive and obvious.

// Bad example

.segmented .focus-bar-button {
  // styles

  &.selected:before,
  &:active:before,
  &.selected + li:before,
  &:active + li:before,
  &:first-child:before {
    display: none
  }
}

Declaration order

Recommended only. During the course of development this might not be realistic. Try to keep the following recommendations in mind.

Related property declarations should be grouped together following the order:

  1. Positioning
  2. Box model
  3. Typographic
  4. Visual

Positioning comes first because it can remove an element from the normal flow of the document and override box model related styles. The box model comes next as it dictates a component's dimensions and placement.

Everything else takes place inside the component or without impacting the previous two sections, and thus they come last.

For a complete list of properties and their order, please see Recess.

.declaration-order {
  /* Positioning */
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 100;

  /* Box-model */
  display: block;
  float: right;
  width: 100px;
  height: 100px;

  /* Typography */
  font: normal 13px "Helvetica Neue", sans-serif;
  line-height: 1.5;
  color: #333;
  text-align: center;

  /* Visual */
  background-color: #f5f5f5;
  border: 1px solid #e5e5e5;
  border-radius: 3px;

  /* Misc */
  opacity: 1;
}

JavaScript (ES6)

Syntax

Use spaces only, with 2 spaces per indentation level. Never mix tabs and spaces.

Try your best to limit all lines to a maximum of 80 characters. Do not ever go over 120.

Do not include trailing whitespace on any lines.

Use only unix-style newlines. \n not \r\n.

Use UTF-8 as the source file encoding.

// bad
function() {
∙∙∙∙const name;
}

// bad
function() {
const name;
}

// good
function() {
∙∙const name;
}

Place 1 space before the leading brace.

// bad
function test(){
  console.log('test');
}

// good
function test() {
  console.log('test');
}

// bad
dog.set('attr',{
  age: '1 year',
  breed: 'Bernese Mountain Dog'
});

// good
dog.set('attr', {
  age: '1 year',
  breed: 'Bernese Mountain Dog'
});

Place 1 space before the opening parenthesis in control statements (if, while etc.). Place no space before the argument list in function calls and declarations.

// bad
function fight () {
  if(isJedi){
    console.log ('Swooosh!');
  }
}

// good
function fight() {
  if (isJedi) {
    console.log('Swooosh!');
  }
}

Set off operators with spaces.

// bad
const x=y+5;

// good
const x = y + 5;

End files with a single newline character.

// bad
(function(global) {
  // ...stuff...
})(this);

// good
(function(global) {
  // ...stuff...
})(this);

Leave a blank line after blocks and before the next statement.

Use a single blank line within the bodies of methods or functions in cases where this improves readability (e.g., for the purpose of delineating logical sections).

// bad
if (foo) {
  return bar;
}
return baz;

// good
if (foo) {
  return bar;
}

return baz;

// bad
const obj = {
  foo() {
  },
  bar() {
  },
};
return obj;

// good
const obj = {
  foo() {
  },

  bar() {
  },
};

return obj;

Do not use leading commas.

Use additional trailing commas.

This leads to cleaner git diffs. Also, transpilers like Babel will remove the additional trailing comma in the transpiled code which means you don't have to worry about the trailing comma problem in legacy browsers.

// bad
const hero = {
    firstName: 'Ada'
  , lastName: 'Lovelace'
  , birthYear: 1815
  , superPower: 'computers'
};

// good
const hero = {
  firstName: 'Ada',
  lastName: 'Lovelace',
  birthYear: 1815,
  superPower: 'computers',
};

Always use explicit semicolons.

// bad
(function() {
  const name = 'Skywalker'
  return name
})()

// good
(() => {
  const name = 'Skywalker';
  return name;
})();

// good (guards against the function becoming an argument when two files with IIFEs are concatenated)
;(() => {
  const name = 'Skywalker';
  return name;
})();

Use braces with all multi-line blocks.

// bad
if (test)
  return false;

// good
if (test) return false;

// good
if (test) {
  return false;
}

// bad
function() { return false; }

// good
function() {
  return false;
}

If you're using multi-line blocks with if and else, put else on the same line as your if block's closing brace.

// bad
if (test) {
  thing1();
  thing2();
}
else {
  thing3();
}

// good
if (test) {
  thing1();
  thing2();
} else {
  thing3();
}

Variables

Use const for all of your references; avoid using var.

This ensures that you can't reassign your references (mutation), which can lead to bugs and difficult to comprehend code.

// bad
a = 1;
var b = 2;

// good
const a = 1;
const b = 2;

If you must mutate references, use let instead of var.

let is block-scoped rather than function-scoped like var.

// bad
var count = 1;

if (true) {
  count += 1;
}

// good, use the let.
let count = 1;

if (true) {
  count += 1;
}

Note that both let and const are block-scoped.

// const and let only exist in the blocks they are defined in.
{
  let a = 1;
  const b = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError

Use one const/let declaration per variable.

// bad
const items = getItems(),
    goSportsTeam = true,
    dragonball = 'z';

// bad
// (compare to above, and try to spot the mistake)
const items = getItems(),
    goSportsTeam = true;
    dragonball = 'z';

// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';

Group all your consts and then group all your lets.

This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables.

// bad
let i, len, dragonball,
    items = getItems(),
    goSportsTeam = true;

// bad
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;

// good
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;

Comparison Operators & Equality

Use === and !== over == and !=.

Conditional statements such as the if statement evaulate their expression using coercion with the ToBoolean abstract method and always follow these simple rules:

if ([0]) {
  // true
  // An array is an object, objects evaluate to true
}

Use shortcuts when possible.

// bad
if (name !== '') {
  // ...stuff...
}

// good
if (name) {
  // ...stuff...
}

// bad
if (collection.length > 0) {
  // ...stuff...
}

// good
if (collection.length) {
  // ...stuff...
}

Type Casting & Coercion

Strings:

//  => this.reviewScore = 9;

// bad
const totalScore = this.reviewScore + '';

// good
const totalScore = String(this.reviewScore);

Use parseInt for Numbers and always with a radix for type casting.

const inputValue = '4';

// bad
const val = new Number(inputValue);

// bad
const val = +inputValue;

// bad
const val = inputValue >> 0;

// bad
const val = parseInt(inputValue);

// good
const val = Number(inputValue);

// good
const val = parseInt(inputValue, 10);

Booleans:

const age = 0;

// bad
const hasAge = new Boolean(age);

// good
const hasAge = Boolean(age);

// good
const hasAge = !!age;

Naming Conventions

Avoid single letter names. Be descriptive with your naming.

// bad
function q() {
  // ...stuff...
}

// good
function query() {
  // ..stuff..
}

Use camelCase when naming objects, functions, and instances.

// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}

// good
const thisIsMyObject = {};
function thisIsMyFunction() {}

Use PascalCase when naming constructors or classes.

// bad
function user(options) {
  this.name = options.name;
}

const bad = new user({
  name: 'nope',
});

// good
class User {
  constructor(options) {
    this.name = options.name;
  }
}

const good = new User({
  name: 'yup',
});

Use a leading underscore _ when naming private properties or methods.

// bad
this.__firstName__ = 'Panda';
this.firstName_ = 'Panda';

// good
this._firstName = 'Panda';

Don't save references to this. Use arrow functions or Function#bind.

// bad
function foo() {
  const self = this;
  return function() {
    console.log(self);
  };
}

// bad
function foo() {
  const that = this;
  return function() {
    console.log(that);
  };
}

// good
function foo() {
  return () => {
    console.log(this);
  };
}

If your file exports a single class, your filename should be exactly the name of the class.

// file contents
class CheckBox {
  // ...
}
export default CheckBox;

// in some other file
// bad
import CheckBox from './checkBox';

// bad
import CheckBox from './check_box';

// good
import CheckBox from './CheckBox';

Use camelCase when you export-default a function. Your filename should be identical to your function's name.

function makeStyleGuide() {
}

export default makeStyleGuide;

Comments

Use /** ... */ for multi-line comments. Include a description, specify types and values for all parameters and return values.

// bad
// make() returns a new element
// based on the passed in tag name
//
// @param {String} tag
// @return {Element} element
function make(tag) {

  // ...stuff...

  return element;
}

// good
/**
 * make() returns a new element
 * based on the passed in tag name
 *
 * @param {String} tag
 * @return {Element} element
 */
function make(tag) {

  // ...stuff...

  return element;
}

Use // for single line comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment.

// bad
const active = true;  // is current tab

// good
// is current tab
const active = true;

// bad
function getType() {
  console.log('fetching type...');
  // set the default type to 'no type'
  const type = this._type || 'no type';

  return type;
}

// good
function getType() {
  console.log('fetching type...');

  // set the default type to 'no type'
  const type = this._type || 'no type';

  return type;
}

Prefixing your comments with FIXME or TODO helps other developers quickly understand if you're pointing out a problem that needs to be revisited, or if you're suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable.

Use // FIXME: to annotate problems.

Use // TODO: to annotate solutions to problems.

class Calculator {
  constructor() {
    // FIXME: shouldn't use a global here
    total = 0;
  }
}

class Calculator {
  constructor() {
    // TODO: total should be configurable by an options param
    this.total = 0;
  }
}

Destructuring

Use object destructuring when accessing and using multiple properties of an object.

// bad
function getFullName(user) {
  const firstName = user.firstName;
  const lastName = user.lastName;

  return `${firstName} ${lastName}`;
}

// good
function getFullName(obj) {
  const { firstName, lastName } = obj;
  return `${firstName} ${lastName}`;
}

// best
function getFullName({ firstName, lastName }) {
  return `${firstName} ${lastName}`;
}

Use array destructuring.

const arr = [1, 2, 3, 4];

// bad
const first = arr[0];
const second = arr[1];

// good
const [first, second] = arr;

Use object destructuring for multiple return values, not array destructuring.

You can add new properties over time or change the order of things without breaking call sites.

// bad
function processInput(input) {
  // then a miracle occurs
  return [left, right, top, bottom];
}

// the caller needs to think about the order of return data
const [left, __, top] = processInput(input);

// good
function processInput(input) {
  // then a miracle occurs
  return { left, right, top, bottom };
}

// the caller selects only the data they need
const { left, right } = processInput(input);

Module Imports

Always use modules (import/export) over a non-standard module system. You can always transpile to your preferred module system.

// bad
const StyleGuide = require('./StyleGuide');
module.exports = StyleGuide.es6;

// ok
import StyleGuide from './StyleGuide';
export default StyleGuide.es6;

// best
import { es6 } from './StyleGuide';
export default es6;

Do not use wildcard imports.

// bad
import * as StyleGuide from './StyleGuide';

// good
import StyleGuide from './StyleGuide';

// good
import { es6 } from './StyleGuide';

Import statements should be grouped in the following order:

  1. Standard library imports (if a standard library exists)
  2. Third party library imports
  3. Local imports (imports specific to this application or library)
// bad
import utils from './utils';
import $ from 'jquery';

// good
import $ from 'jquery';
import utils from './utils';

Strings

Use single quotes '' for strings.

// bad
const name = "Capt. Janeway";

// good
const name = 'Capt. Janeway';

When programmatically building up strings, use template strings instead of concatenation.

Template strings give you a readable, concise syntax with proper newlines and string interpolation features.

// bad
function sayHi(name) {
  return 'How are you, ' + name + '?';
}

// bad
function sayHi(name) {
  return ['How are you, ', name, '?'].join();
}

// good
function sayHi(name) {
  return `How are you, ${name}?`;
}

Arrays

Use the literal syntax for array creation.

// bad
const items = new Array();

// good
const items = [];

Use Array#push instead of direct assignment to add items to an array.

const someStack = [];

// bad
someStack[someStack.length] = 'abracadabra';

// good
someStack.push('abracadabra');

Use array spreads ... to copy arrays.

// bad
const len = items.length;
const itemsCopy = [];
let i;

for (i = 0; i < len; i++) {
  itemsCopy[i] = items[i];
}

// good
const itemsCopy = [...items];

To convert an array-like object to an array, use Array#from.

const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);

Functions

Use function declarations instead of function expressions.

Function declarations are named, so they're easier to identify in call stacks. Also, the whole body of a function declaration is hoisted, whereas only the reference of a function expression is hoisted. This rule makes it possible to always use Arrow Functions in place of function expressions.

// bad
const foo = function () {
};

// good
function foo() {
}

Immediately-invoked function expressions:

(() => {
  console.log('Welcome to the Internet. Please follow me.');
})();

Never declare a function in a non-function block (if, while, etc). Assign the function to a variable instead. Browsers will allow you to do it, but they all interpret it differently.

// bad
if (currentUser) {
  function test() {
    console.log('Nope.');
  }
}

// ok
let test;
if (currentUser) {
  test = () => {
    console.log('Yup.');
  };
}

Never name a parameter arguments. This will take precedence over the arguments object that is given to every function scope.

// bad
function nope(name, options, arguments) {
  // ...stuff...
}

// good
function yup(name, options, args) {
  // ...stuff...
}

Never use arguments, opt to use rest syntax ... instead.

// bad
function concatenateAll() {
  const args = Array.prototype.slice.call(arguments);
  return args.join('');
}

// good
function concatenateAll(...args) {
  return args.join('');
}

Use default parameter syntax rather than mutating function arguments.

Use Object#assign or jQuery#extend to set default object values.

// bad
function handleThings(options) {
  // No! We shouldn't mutate function arguments.
  // Double bad: if opts is falsy it'll be set to an object which may
  // be what you want but it can introduce subtle bugs.
  this.options = options || {};
  // ...
}

// good
function handleThings(options = {}) {
  // ...
}

// good
function handleThings(options = {}) {
  this.options = Object.assign({}, {
    valueA: 'AAA', // defaults
  }, options);
}

When you must use function expressions (as when passing an anonymous function), use arrow function notation.

It creates a version of the function that executes in the context of this, which is usually what you want, and is a more concise syntax.

If you have a fairly complicated function, you might move that logic out into its own function declaration.

If the function body fits on one line and there is only a single argument, feel free to omit the braces and parentheses, and use the implicit return. Otherwise, add the parentheses, braces, and use a return statement.

// bad
[1, 2, 3].map(function (x) {
  return x * x;
});

// good
[1, 2, 3].map((x) => {
  return x * x;
});

// good
[1, 2, 3].map(x => x * x);

// good
[1, 2, 3].reduce((total, n) => {
  return total + n;
}, 0);

Objects

Use the literal syntax for object creation.

// bad
const item = new Object();

// good
const item = {};

Don't use reserved words as keys. It won't work in IE8. More info.

Use readable synonyms in place of reserved words.

// bad
const superman = {
  default: { clark: 'kent' },
  private: true,
  klass: 'alien',
};

// good
const superman = {
  defaults: { clark: 'kent' },
  hidden: true,
  type: 'alien',
};

Use computed property names when creating objects with dynamic property names.

They allow you to define all the properties of an object in one place.

function getKey(k) {
  return `a key named ${k}`;
}

// bad
const obj = {
  name: 'San Francisco',
};
obj[getKey('enabled')] = true;

// good
const obj = {
  name: 'San Francisco',
  [getKey('enabled')]: true,
};

Use object method shorthand.

// bad
const atom = {
  value: 1,

  addValue: function (value) {
    return atom.value + value;
  },
};

// good
const atom = {
  value: 1,

  addValue(value) {
    return atom.value + value;
  },
};

Use property value shorthand.

Group your shorthand properties at the beginning of your object declaration.

const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';

// bad
const obj = {
  episodeOne: 1,
  twoJedisWalkIntoACantina: 2,
  lukeSkywalker,
  episodeThree: 3,
  mayTheFourth: 4,
  anakinSkywalker,
};

// good
const obj = {
  lukeSkywalker,
  anakinSkywalker,
  episodeOne: 1,
  twoJedisWalkIntoACantina: 2,
  episodeThree: 3,
  mayTheFourth: 4,
};

Use dot notation when accessing properties.

Use subscript notation [] when accessing properties with a variable.

const luke = {
  jedi: true,
  age: 28,
};

// bad
const isJedi = luke['jedi'];

// good
const isJedi = luke.jedi;

// good
function getProp(prop) {
  return luke[prop];
}

const isJedi = getProp('jedi');

Classes

Always use class. Avoid manipulating prototype directly.

class syntax is more concise and easier to reason about.

// bad
function Queue(contents = []) {
  this._queue = [...contents];
}
Queue.prototype.pop = function() {
  const value = this._queue[0];
  this._queue.splice(0, 1);
  return value;
}

// good
class Queue {
  constructor(contents = []) {
    this._queue = [...contents];
  }

  pop() {
    const value = this._queue[0];
    this._queue.splice(0, 1);
    return value;
  }
}

Use extends for inheritance.

It is a built-in way to inherit prototype functionality without breaking instanceof.

// bad
const inherits = require('inherits');
function PeekableQueue(contents) {
  Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function() {
  return this._queue[0];
}

// good
class PeekableQueue extends Queue {
  peek() {
    return this._queue[0];
  }
}

It's okay to write a custom toString() method, just make sure it works successfully and causes no side effects.

class Jedi {
  contructor(options = {}) {
    this.name = options.name || 'no name';
  }

  getName() {
    return this.name;
  }

  toString() {
    return `Jedi - ${this.getName()}`;
  }
}

Accessor functions for properties are not required.

If you do make accessor functions use getVal() and setVal('hello').

If the property is a boolean, use isVal() or hasVal().

It's okay to create get() and set() functions, but be consistent.

// bad
dragon.age();
dragon.age(25);

// good
dragon.getAge();
dragon.setAge(25);

// bad
if (!dragon.born()) {
  return false;
}

// good
if (!dragon.isBorn()) {
  return false;
}

// ok
class Jedi {
  constructor(options = {}) {
    const lightsaber = options.lightsaber || 'blue';
    this.set('lightsaber', lightsaber);
  }

  set(key, val) {
    this[key] = val;
  }

  get(key) {
    return this[key];
  }
}

Events

When attaching data payloads to events (whether DOM events or something more proprietary like Backbone events), pass a hash instead of a raw value. This allows a subsequent contributor to add more data to the event payload without finding and updating every handler for the event.

// bad
$(this).trigger('listingUpdated', listing.id);

...

$(this).on('listingUpdated', function(e, listingId) {
  // do something with listingId
});
prefer:

// good
$(this).trigger('listingUpdated', { listingId : listing.id });

...

$(this).on('listingUpdated', function(e, data) {
  // do something with data.listingId
});

jQuery

Prefix jQuery object variables with a $.

// bad
const sidebar = $('.sidebar');

// good
const $sidebar = $('.sidebar');

Cache jQuery lookups.

// bad
function setSidebar() {
  $('.sidebar').hide();

  // ...stuff...

  $('.sidebar').css({
    'background-color': 'pink'
  });
}

// good
function setSidebar() {
  const $sidebar = $('.sidebar');
  $sidebar.hide();

  // ...stuff...

  $sidebar.css({
    'background-color': 'pink'
  });
}

Liquid

An Introduction to Shopify

Shopify is the only platform that we develop for that uses Liquid. Fortunately, they've done a great job on documentation and almost all of the info that you need can be found there.

The Objects link above is almost certainly the most important resource available to you when writing code for Shopify. It contains every single object you might come across during development and all of the values attached to them.

Syntax

{% if boolean %}
  <div class="special-case">
    <!-- content -->
  </div>
{% endif %}

Whitespace in Expressions and Statements

{% if boolean %} <!-- Yes -->
{%if boolean%} <!-- No -->

<!-- Yes -->
{% unless string == 'foobar' %}
  {{ string }}
{% endunless %}

<!-- No -->
{%unless string=='foobar' %}
{{string}}
{%endunless%}

Variables

<!-- Yes -->
{% assign some_boolean = true %}
{% capture some_string %}This is a string.{% endcapture %}

<!-- No -->
{% assign some-boolean = true %}
{% assign pleaseNo = true %}
{% capture sillyString %}This is a string.{% endcapture %}

<!-- Move verbose logic into a variable -->
{% assign slides_class = 'no-slides' %}
{% if settings.slide_1 or settings.slide_2 or settings.slide_3 or settings.slide_4 or settings.slide_5 %}
  {% assign slides_class = 'has-slides' %}
{% endif %}

<body class="{{ slides_class }}">

Debugging Tips

<!-- console.log() an object with the `| json` filter -->
<script charset="utf-8">
  console.log({{ someObject | json }});
</script>

<!-- create a theme settings javascript object -->
<script charset="utf-8">
  window.ThemeName = {};
  ThemeName.settings = {{ settings | json }};
</script>

PHP

Introduction

Outside of the WordPress environment, Pixel Union follows PSR-2 standards for PHP development. This is an overview of the higher-level rules for PSR-2, for the full set of standards please reference the PHP-FIG docs.

the PSR-2 Standard extends the PSR-1 Standard, documented here.

Note: The one way in which we diverge from PSR-2 is that we use 2 spaces for indents instead of 4. This is to maintain consistency between the other code styles, which are all 2 spaces.

Basics

<?php
namespace Vendor\Package;

use FooInterface;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

class Foo extends Bar implements FooInterface
{

  const SAMPLE_CONSTANT = 12345678;

  public function sampleMethod($a, $b = null)
  {
    if ($a === $b) {
      bar();
    } elseif ($a > $b) {
      $foo->bar($arg1);
    } else {
      BazClass::bar($arg2, $arg3);
    }
  }

  final public static function bar()
  {
    // method body
  }
}

Namespace and Use Declarations

<?php
namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

/* additional PHP code */

Method and Function Calls

When making a method or function call:

Argument lists can be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list should be on its own line, with only one argument per line.

<?php

// all good
bar();
$foo->bar($arg1);
Foo::bar($arg2, $arg3);

// also good
$foo->bar(
  $longArgument,
  $longerArgument,
  $muchLongerArgument
);

Control Structures

The general style rules for control structures are as follows:

The body of each structure should be enclosed by braces. This standardizes how the structures look, and reduces the likelihood of introducing errors as new lines get added to the body.

<?php

// great
if ($expr1) {
  // if body
} elseif ($expr2) {
  // elseif body
} else {
  // else body;
}


// terrific
foreach ($iterable as $key => $value) {
  // foreach body
}

// superb
switch ($expr) {
  case 0:
    echo 'First case, with a break';
    break;
  case 1:
    echo 'Second case, which falls through';
    // no break
  case 2:
  case 3:
  case 4:
    echo 'Third case, return instead of break';
    return;
  default:
    echo 'Default case';
    break;
}

Closures

Like methods and functions, argument lists can be spread across multiple lines, using the same line-break and indentation style.

<?php

$closureWithArgs = function ($arg1, $arg2) {
  // body
};

$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) {
  // body
};

$longArgs_longVars = function (
    $longArgument,
    $longerArgument,
    $muchLongerArgument
) use (
    $longVar1,
    $longerVar2,
    $muchLongerVar3
) {
    // body
};

WordPress

WordPress and Pixel Union

We have taken WordPress's PHP Coding Standards guide and placed it here. Fun.

Note:

Some parts of the WordPress code structure for PHP markup are inconsistent in their style. WordPress is working to gradually improve this by helping users maintain a consistent style so the code can become clean and easy to read at a glance.

Single and Double Quotes

Use single and double quotes when appropriate. If you’re not evaluating anything in the string, use single quotes. You should almost never have to escape quotes in a string, because you can just alternate your quoting style.

Text that goes into attributes should be run through esc_attr() so that single or double quotes do not end the attribute value and invalidate the HTML and cause a security issue. See Data Validation in the Codex for further details.

<?php

echo '<a href="/static/link" title="Yeah yeah!">Link name</a>';
echo "<a href='$link' title='$linktitle'>$linkname</a>";

?>

Indentation

Brace Style

<?php

if ( condition ) {
  action1();
  action2();
} elseif ( condition2 && condition3 ) {
  action3();
  action4();
} else {
  defaultaction();
}

// Even if the braces aren't necessary

if ( condition ) {
  action0();
}

foreach ( $items as $item ) {
  process_item( $item );
}

// Template files should use the alternative syntax
?>

<?php if ( is_singular() ) : ?>
  <?php edit_post_link( __( 'Edit', 'ns' ) ); ?>
<?php endif; ?>

<?php if ( have_posts() ) : ?>
  <?php while ( have_posts() ) : the_post(); ?>
    <?php get_template_part( 'content', get_post_format() ); ?>
  <?php endwhile; ?>
<?php else : ?>
  <?php get_template_part( 'content', 'none' ); ?>
<?php endif; ?>

Function Guards

Most theme-specific functions should be wrapped in a function_exists test to allow child themes to override them. The function guard should use the alternative if endif syntax.

<?php
// Function guards should use if/endif

if ( ! function_exists( 'ns_styles' ) ) :
/**
 * Comments should be just above the function.
 *
 * The function isn't indented.
 */
function core2_styles() {
  wp_enqueue_style( 'ns-style', get_stylesheet_uri() );
}
endif;
add_action( 'wp_enqueue_scripts', 'ns_styles' ); // Outside of the guard

Regular Expressions

Perl compatible regular expressions (PCRE, preg_ functions) should be used in preference to their POSIX counterparts. Never use the /e switch, use preg_replace_callback instead.

It’s most convenient to use single-quoted strings for regular expressions since, contrary to double-quoted strings, they have only two metasequences: \' and \\.

Space Usage

<?php

# No
x==23
foreach($foo as $bar) { ...

# Yes
x == 23
foreach ( $foo as $bar ) { ...

# Function definition
function my_function( $param1 = 'foo', $param2 = 'bar' ) { ...

# Function call
my_function( $param1, func_param( $param2 ) );

# Comparisons
if ( ! $foo ) { ...

# Type casting
foreach ( (array) $foo as $bar ) { ...

$foo = (boolean) $bar;

# Array items
$x = $foo['bar']; // Yes
$x = $foo[ 'bar' ]; // No

$x = $foo[0]; // Yes
$x = $foo[ 0 ]; // No

$x = $foo[ $bar ]; // Yes
$x = $foo[$bar]; // No

Formatting SQL statements

When formatting SQL statements you may break it into several lines and indent if it is sufficiently complex to warrant it. Most statements work well as one line though. Always capitalize the SQL parts of the statement like UPDATE or WHERE.

Functions that update the database should expect their parameters to lack SQL slash escaping when passed. Escaping should be done as close to the time of the query as possible, preferably by using $wpdb->prepare()

$wpdb->prepare() is a method that handles escaping, quoting, and int-casting for SQL queries. It uses a subset of the sprintf() style of formatting.

%s is used for string placeholders and %d is used for integer placeholders. Note that they are not ‘quoted’! $wpdb->prepare() will take care of escaping and quoting for us. The benefit of this is that we don’t have to remember to manually use esc_sql(), and also that it is easy to see at a glance whether something has been escaped or not, because it happens right when the query happens.

See Data Validation in the Codex for more information.

<?php 

$var = "dangerous'"; // raw data that may or may not need to be escaped
$id = some_foo_number(); // data we expect to be an integer, but we're not certain

$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_title = %s WHERE ID = %d", $var, $id ) );

Database Queries

Avoid touching the database directly. If there is a defined function that can get the data you need, use it. Database abstraction (using functions instead of queries) helps keep your code forward-compatible and, in cases where results are cached in memory, it can be many times faster.

If you must touch the database, get in touch with some developers by posting a message to the wp-hackers mailing list. They may want to consider creating a function for the next WordPress version to cover the functionality you wanted.

Naming Conventions

Use lowercase letters in variable, action, and function names (never camelCase). Separate words via underscores. Don’t abbreviate variable names un-necessarily; let the code be unambiguous and self-documenting.

Class file names should be based on the class name with class- prepended and the underscores in the class name replaced with hyphens.

This file-naming standard is for all current and new files with classes. There is one exception for three files that contain code that got ported into BackPress: class.wp-dependencies.php, class.wp-scripts.php, class.wp-styles.php. Those files are prepended with class., a dot after the word class instead of a hyphen.

Files containing template tags in wp-includes should have -template appended to the end of the name so that they are obvious.

<?php

# Bad
function someName( $VARIABLE ) { [...] }

# Good
function some_name( $some_variable ) { [...] }

# Classes
class Walker_Category extends Walker { [...] }
class WP_HTTP { [...] }

# File names
"my-plugin-name.php" // Good
"my_pluginname.php" // Bad

"class-wp-error.php" // Good
"general-template.php" // Good

Self-Explanatory Flag Values for Function Arguments

Prefer string values to just true and false when calling functions.

Since PHP doesn’t support named arguments, the values of the flags are meaningless, and each time we come across a function call like the examples above, we have to search for the function definition. The code can be made more readable by using descriptive string values, instead of booleans.

<?php

# Bad
function eat( $what, $slowly = true ) {
...
}
eat( 'mushrooms' );
eat( 'mushrooms', true ); // what does true mean?
eat( 'dogfood', false ); // what does false mean? The opposite of true?

# Good
function eat( $what, $speed = 'slowly' ) {
...
}
eat( 'mushrooms' );
eat( 'mushrooms', 'slowly' );
eat( 'dogfood', 'quickly' );

Ternary Operator

Ternary operators are fine, but always have them test if the statement is true, not false. Otherwise, it just gets confusing. (An exception would be using ! empty(), as testing for false here is generally more intuitive.)

<?php

// (if statement is true) ? (do this) : (else, do this);
$musictype = ( 'jazz' == $music ) ? 'cool' : 'blah';
// (if field is not empty ) ? (do this) : (else, do this);

Yoda Conditions

When doing logical comparisons, always put the variable on the right side, constants or literals on the left.

In the above example, if you omit an equals sign (admit it, it happens even to the most seasoned of us), you’ll get a parse error, because you can’t assign to a constant like true. If the statement were the other way around ( $the_force = true ), the assignment would be perfectly valid, returning 1, causing the if statement to evaluate to true, and you could be chasing that bug for a while.

A little bizarre, it is, to read. Get used to it, you will.

This applies to ==, !=, ===, and !==. Yoda conditions for <, >, <= or >= are significantly more difficult to read and are best avoided.

<?php

if ( true == $the_force ) {
  $victorious = you_will( $be );
}

Miscellaneous

Bad
<? ... ?>
<?= $var ?>

Good
<?php ... ?>
<?php echo $var; ?>

<?php

# Bad – clever but not immediately clear
isset( $var ) || $var = some_function();

# Good – longer but clearer
if ( ! isset( $var ) ) {
  $var = some_function();
}

CoffeeScript

Code layout

Blank lines

Trailing Whitespace

Encoding

Module Imports

If using a module system (CommonJS Modules, AMD, etc.), require statements should be placed on separate lines at the top of the file.

These statements should be grouped in the following order:

  1. Standard library imports (if a standard library exists)
  2. Third party library imports
  3. Local imports (imports specific to this application or library)
require "lib/setup"
Backbone = require "backbone"

Whitespace in Expressions and Statements

Avoid extraneous whitespace in the following situations:

Additional recommendations::

$("body") # Yes
$( "body" ) # No

console.log x, y # Yes
console.log x , y # No

test: (param = null) -> # Yes
test: (param=null) -> # No

# Yes
x = 1
y = 1
fooBar = 3

# No
x      = 1
y      = 1
fooBar = 3

Comments

If modifying code that is described by an existing comment, update the comment such that it accurately reflects the new code. (If possible, improve the code to be clear and concise, and delete the comment entirely.)

The first word of the comment should be capitalized, unless the first word is an identifier that begins with a lower-case letter.

If a comment is short, the period at the end can be omitted.

Block Comments

Inline Comments

# This is a block comment. Note that if this were a real block
# comment, we would actually be describing the proceeding code.
#
# This is the second paragraph of the same block comment. Note
# that this paragraph was separated from the previous paragraph
# by a line containing a single comment character.

init()
start()
stop()

# No
x = x + 1 # Increment x

# Yes
x = x + 1 # Compensate for border

Naming Conventions

Use camelCase (with a leading lowercase character) to name all variables, methods, and object properties.

Use UpperCamelCase (with a leading uppercase character) to name all classes.

(The official CoffeeScript convention is camelCase, because this simplifies interoperability with JavaScript. For more on this decision, see [here][coffeescript-issue-425].)

Methods and variables that are intended to be "private" should begin with a leading underscore.

# No
my_awesome_var = 0

# Yes
myAwesomeVar = 0

_privateMethod: ->

Functions

(These guidelines also apply to the methods of a class.)

foo = (arg1, arg2) -> # Yes
foo = (arg1, arg2)-> # No

# chained functions
[1..3]
    .map((x) -> x * x)
    .concat([10..12])
    .filter((x) -> x < 11)
    .reduce((x, y) -> x + y)

foo(4).bar(8) # Yes
foo(4).bar 8 # No

Strings

"this is an #{adjective} string" # Yes
"this is an " + adjective + " string" # No

Conditionals

foo() unless bar # Yes
foo() if not bar # No
foo() if !bar # No

# Yes
if true
    ...
else
    ...

# No
unless false
    ...
else
    ...

# No
if true then ...
else ...

Looping and Comprehensions

# Yes
result = (item.name for item in array)

# No
results = []
for item in array
    results.push item.name

# Filtering
result = (item for item in array when item.name is "test")

# Iterate over the keys and values of objects
object = one: 1, two: 2
alert("#{key} = #{value}") for key, value of object

Annotations

Annotation types:

If a custom annotation is required, the annotation should be documented in the project's README.

# FIXME: The client's current state should *not* affect payload processing.
resetClientState()
processPayload()

Miscellaneous

Array::slice # Yes
Array.prototype.slice # No

return @property # Yes
return this.property # No

return this # Yes
return @ # No

console.log args... # Yes

(a, b, c, rest...) -> # Yes

Git

An introduction to Git

At its most basic, source control gives us the ability to store, share, and transport code and code changes, but the real value comes from the ability to record and replay the stories behind each change during the lifetime of a project.

This is not an intro to source control or a git how-to: we assume that you have an intermediate understanding of how to use git. If you’re new to git, chapters 2, 3, and 6 (through the section on interactive rebasing) of Scott Chacon’s git book will tell you the how.

It doesn’t matter if you use git’s commands or a GUI (see GitHub for Mac and Gitbox), but you need a confident understanding of what’s happening regardless. Knowing the commands often helps.

Repo Naming

To keep our many repositories easy to find we follow a simple naming convention:

# Platforms

Shopify      shopify
WordPress    wp
Tumblr       tumblr
Ghost        ghost

# Examples

grid-shopify
  The Grid Shopify theme.

bindery-wp
  The Bindery WordPress theme.

shopify-skeleton
  Our shopify theme framework.

wp-calcium
  Our WordPress build system.

grid-johndoe-shopify
  A customization of the Grid Shopify theme for John Doe (a client).

Setup

Things to do right away:

git config --global user.name "Your Name"
git config --global user.email "name@pixelunion.net"

git config --global branch.autosetuprebase always

git config --global push.default simple

# To avoid waking up in a cold sweat mumbling vi commands...
# Use Sublime Text 2 (when installed) as your default editor.
git config --global core.editor "subl -n -w"

Branches and Pull Requests

Never commit directly to master. Instead, create small branches for each and every topic you work on (such as a feature or bug fix), and create a pull request into master when the branch is complete.

The exception to this rule is version commits and tags.

Naming

Long-Running Branches

If your branch cannot be named as a specific function/feature/bug then you can create a long-running branch with a name like multiple_fixes.

When using this method, it's important that the commits are clear and minimal (see Commits).

# Bad
git checkout -b newBranch
git checkout -b v0.0.4-dev

# Good
git checkout -b home_slideshow
git checkout -b fix_missing_video

# OK
git checkout -b multiple_fixes

Commits

Please read Stephen Ball’s Steel City Ruby 2013 presentation, Deliberate Git. It covers this topic very well.

Once you’ve read it, you’ll understand why you should:

When writing commits:

# Bad commit message
git commit -m "Fix login bug"

# Good commit message
git commit -m "Redirect user to the requested page after login"

# Great commit message
git commit
# opens $EDITOR
"Redirect user to the requested page after login

Users were being redirected to the home page after login, which is less
useful than redirecting to the page they had originally requested before
being redirected to the login form."

Pushing

If you’re pushing a new branch, use the --set-upstream (-u) flag to automatically set the remote branch as your local branch’s tracking branch.

git push -u origin your_branch

Merging

So your pull request has been peer-reviewed and approved – nice work! Now it's time to merge your changes into master.

You want to use a fast-forward merge (git merge --ff-only). Merging this way prevents "merge bubbles" where commit messages are created that say that a branch has been merged. This is called a recursive merge and it muddies up the commit history.

# fast-forward merge
git checkout master
git merge --ff-only your_branch

# Didn't let you do it? No problem, it probably means that your
# branch is out of date. In other words, master has been updated
# since you originally branched off of it.
git checkout your_branch
git rebase master
git push -f
git checkout master
git merge --ff-only your_branch

# Whoa, did you just force push and re-write history?
# Ya, I did. At Pixel Union a branch is your own. You own it. It's your baby.
# If somebody was working on your branch, you'd know about it because they
# would have talked to you.

# Special note: never re-write master's history.

Tagging and Releasing

Versioned libraries, both internal and public, should follow these practices:

# on master
git add <your files>
git commit -m "Version bump to v1.1.0"
git tag -a v1.1.0 -m "Version v1.1.0 release"
git push && git push --tags

# Bump helper
# Place this in your bash profile or .zshrc
bump() { git commit -m "Bump to $1" && git tag -a $1 -m "Bump version $1"; }

# ooo, that's snazzy
# use it like this:
git add <your files>
bump v1.1.0
git push && git push --tags

# Version numbering scheme

Minor bug fix: v1.0.0 -> v1.0.1
Major bug fix: v1.0.0 -> v1.1.0
Minor feature: v1.0.0 -> v1.1.0
Major feature: v1.0.0 -> v2.0.0