Frontend Standards

Low-hanging fruit

The long-term objective is that all the code we produce has a single voice. This means easier code maintenance in the future, and increases portability.

General

  • Use four spaces for tab indentation.
  • Be a good citizen. Consider your colleagues now and in the future.

HTML

  • Double-quote all attributes which require values

  • Don’t use values for Boolean attributes

  • Don’t use closing slashes on empty elements

    1
    <img src="foo.png" hidden>
    
  • For styling, a class is always preferred to an id; reserve the id for truly unique features such as an attached JS event.

  • Class names should be lowercase and hyphenated.

  • If adding a class just as a JS hook, use the js- prefix on the name.

    1
    <div id="unique-1" class="component js-trigger"></div>
    
  • Always use appropriate elements for the task at hand; for example, always use a button to submit a form, never another element that looks like a button but has behaviour added with script.

    1
    2
    3
    4
    5
    //Never this
    <a onclick="submit()" class="button">Submit</a> 
    
    //Only this
    <button type="submit">Submit</button>
    

CSS

  • Order rules by property group, and break each rule onto a separate line.

  • Leave a single space between the colon and the first value.

  • Single-quote all string values, including inside the URL function.

  • Except where specifically required (e.g. a time value for transitions), do not use a unit with a zero value; 0px is the same as 0em, so simply use 0.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    div {
    /* Position */
        z-index: 100px;
    /* Box Model */
        margin: 0;
    /* Appearance */
        background-image: url('foo.png');
    /* Behaviour */
        animation: foo 1s;
    }
    
  • Don’t use id selectors to apply rules.

  • Use a new line for each selector.

    1
    2
    3
    4
    5
    6
    /* Not this */
    #foo, .bar { }
    
    /* Only this */
    .foo,
    .bar { }
    
  • When using class names, don’t specify the element in the selector unless there is a specific reason for it (e.g. specificity)

    1
    2
    3
    4
    5
    /* Not this */
    div.foo { } 
    
    /* Only this */
    .foo { }
    
  • Avoid !important wherever humanly possible.

  • Don’t use long selector chains; if you’re going past two selectors, consider using a new class instead.

    1
    2
    3
    4
    5
    /* Not this */
    div ul li a { } 
    
    /* Only this */
    .list-link { }
    
  • When listing vendor prefixes, always have the unprefixed property name last.

    1
    2
    -webkit-transform: none;
    transform: none;
    

Sass

  • Keep all variables in a single variables file and use generic names.

  • Using @import creates global scope so avoid duplicating variable names, even between variables and maps.

  • Like classes, variables can be used in multiple places, and their function can change. An exception to this would be if you’re using loops and require a variable specifically for the current scope.

    1
    2
    3
    4
    5
    /* Not this */
    $textWhite: #fff; 
    
    /* Only this */
    $keyColorMain: #fff;
    
  • Do not nest more than three levels deep. While nesting is powerful, it can have a negative impact on readability and, therefore, maintainability.

  • Use source maps for easier debugging (http://devtoolsecrets.com/secret/editing-use-sass-source-maps.html).

  • If not using Compass or an autoprefixer, make a Mixin for any CSS property which requires vendor prefixes.

    1
    2
    3
    4
    @mixin transform($args…) {
        -webkit-transform: $args;
        transform: $args;
    }
    
  • Consider placeholder selectors for repetitive code instead of extending other typed classes. Placeholder selectors will not be written to the stylesheet.

    1
    2
    3
    4
    5
    6
    7
    8
    %gutter {
        margin:0;
        padding:0;
    }
    .btn {
        @extend %gutter;
        background: #c9c9c9;
    }
    

JavaScript

  • Always use var to declare variables.

  • Each variable on its own line.

  • Outer-encase all strings in single-quotes.

  • Defined names should be camelCased.

  • Use an underscore prefix to name private variables.

  • Variables with a Boolean value should be prefixed with is.

    1
    2
    3
    var _foo = 'Hello World';
    var _barBaz = 1234;
    var isBoolean = true;
    
  • Use line breaks to show the contents of a function or conditional statement.

    1
    2
    3
    4
    5
    6
    7
    // Not this
    if(this){that;}
    
    // Only this
    if (this) {
        that;
    }
    

Do not perform calculations or access the DOM when defining loops.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Not this
for (var i=0; i < (foo * 5); i++) {alert(i);}

// Only this
var fooTotal = foo * 5;
for (var i=0; i < fooTotal; i++) { alert(i); }

// Not this
for (var i=0; i < $('.foo').length); i++) {alert(i);}

// Only this
for (var i=0,fooLen = $('.foo').length; i < fooLen; i++) { alert(i); }
  • Lint your code automatically if your text editor allows, or manually if not. Use JSHint rules (http://jshint.com/).

Comments

  • Comment everything, all the time; all code should be minified before going into production, so trying to save space at this point is a false economy.

  • Frontend code comments should follow phpDocumentor (http://bit.ly/3FPH7g) standards.

  • For CSS, comment uncommon practices or decisions.

  • Comment class methods and loose functionality, along with any other complex logic that may benefit from them.

  • Document the parameters and return types of your methods and write an accurate description of the purpose of the method.

  • If the method is complex and has multiple use syntaxes, document them as examples in the comment block.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // Ensures value can't go below zero or beyond the maximum possible value.
    var newX = Math.min(maxDrag, Math.max(0, newX));
    /**
     * Filters the data source to create a subset matching the chosen date.
     *
     * @param string requestDate - Following YYYY-MM-DD syntax.
     * @return array.
     */
    filterByDate: function(requestDate, implementOffset) {
    }
    /**
     * Returns a User record along with nested Goal records, recent
     * activity and any notifications to be shown.
     *
     * Example Usage:
     * APIWrapper.getUserDetails({ facebookToken: '123456' });
     *
     * @param object requestData - Contain either Facebook or Instagram tokens.
     * @return object - jQuery promise (resolved) with User record.
     */
    getUserDetails: function(requestData) {
    }