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) { }