Blog post
Published:
…
Repository: bendc/frontend-guidelines Branch: master Commit: a6e4857b728b Files: 1 Total size: 15.7 KB Directory structure: gitextract_505rydb_/ └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================ # Frontend Guidelines ## HTML ### Semantics HTML5 provides us with lots of semantic elements aimed to describe precisely the content. Make sure you benefit from its rich vocabulary. ```html
Published: 21st Feb, 2015
…
Published:
…


...
...
``` ## CSS ### Semicolons While the semicolon is technically a separator in CSS, always treat it as a terminator. ```css /* bad */ div { color: red } /* good */ div { color: red; } ``` ### Box model The box model should ideally be the same for the entire document. A global `* { box-sizing: border-box; }` is fine, but don't change the default box model on specific elements if you can avoid it. ```css /* bad */ div { width: 100%; padding: 10px; box-sizing: border-box; } /* good */ div { padding: 10px; } ``` ### Flow Don't change the default behavior of an element if you can avoid it. Keep elements in the natural document flow as much as you can. For example, removing the white-space below an image shouldn't make you change its default display: ```css /* bad */ img { display: block; } /* good */ img { vertical-align: middle; } ``` Similarly, don't take an element off the flow if you can avoid it. ```css /* bad */ div { width: 100px; position: absolute; right: 0; } /* good */ div { width: 100px; margin-left: auto; } ``` ### Positioning There are many ways to position elements in CSS. Favor modern layout specifications such as Flexbox and Grid, and avoid removing elements from the normal document flow, for example with `position: absolute`. ### Selectors Minimize selectors tightly coupled to the DOM. Consider adding a class to the elements you want to match when your selector exceeds 3 structural pseudo-classes, descendant or sibling combinators. ```css /* bad */ div:first-of-type :last-child > p ~ * /* good */ div:first-of-type .info ``` Avoid overloading your selectors when you don't need to. ```css /* bad */ img[src$=svg], ul > li:first-child { opacity: 0; } /* good */ [src$=svg], ul > :first-child { opacity: 0; } ``` ### Specificity Don't make values and selectors hard to override. Minimize the use of `id`'s and avoid `!important`. ```css /* bad */ .bar { color: green !important; } .foo { color: red; } /* good */ .foo.bar { color: green; } .foo { color: red; } ``` ### Overriding Overriding styles makes selectors and debugging harder. Avoid it when possible. ```css /* bad */ li { visibility: hidden; } li:first-child { visibility: visible; } /* good */ li + li { visibility: hidden; } ``` ### Inheritance Don't duplicate style declarations that can be inherited. ```css /* bad */ div h1, div p { text-shadow: 0 1px 0 #fff; } /* good */ div { text-shadow: 0 1px 0 #fff; } ``` ### Brevity Keep your code terse. Use shorthand properties and avoid using multiple properties when it's not needed. ```css /* bad */ div { transition: all 1s; top: 50%; margin-top: -10px; padding-top: 5px; padding-right: 10px; padding-bottom: 20px; padding-left: 10px; } /* good */ div { transition: 1s; top: calc(50% - 10px); padding: 5px 10px 20px; } ``` ### Language Prefer English over math. ```css /* bad */ :nth-child(2n + 1) { transform: rotate(360deg); } /* good */ :nth-child(odd) { transform: rotate(1turn); } ``` ### Vendor prefixes Kill obsolete vendor prefixes aggressively. If you need to use them, insert them before the standard property. ```css /* bad */ div { transform: scale(2); -webkit-transform: scale(2); -moz-transform: scale(2); -ms-transform: scale(2); transition: 1s; -webkit-transition: 1s; -moz-transition: 1s; -ms-transition: 1s; } /* good */ div { -webkit-transform: scale(2); transform: scale(2); transition: 1s; } ``` ### Animations Favor transitions over animations. Avoid animating other properties than `opacity` and `transform`. ```css /* bad */ div:hover { animation: move 1s forwards; } @keyframes move { 100% { margin-left: 100px; } } /* good */ div:hover { transition: 1s; transform: translateX(100px); } ``` ### Units Use unitless values when you can. Favor `rem` if you use relative units. Prefer seconds over milliseconds. ```css /* bad */ div { margin: 0px; font-size: .9em; line-height: 22px; transition: 500ms; } /* good */ div { margin: 0; font-size: .9rem; line-height: 1.5; transition: .5s; } ``` ### Colors If you need transparency, use `rgba`. Otherwise, always use the hexadecimal format. ```css /* bad */ div { color: hsl(103, 54%, 43%); } /* good */ div { color: #5a3; } ``` ### Drawing Avoid HTTP requests when the resources are easily replicable with CSS. ```css /* bad */ div::before { content: url(white-circle.svg); } /* good */ div::before { content: ""; display: block; width: 20px; height: 20px; border-radius: 50%; background: #fff; } ``` ### Hacks Don't use them. ```css /* bad */ div { // position: relative; transform: translateZ(0); } /* good */ div { /* position: relative; */ will-change: transform; } ``` ## JavaScript ### Performance Favor readability, correctness and expressiveness over performance. JavaScript will basically never be your performance bottleneck. Optimize things like image compression, network access and DOM reflows instead. If you remember just one guideline from this document, choose this one. ```javascript // bad (albeit way faster) const arr = [1, 2, 3, 4]; const len = arr.length; var i = -1; var result = []; while (++i < len) { var n = arr[i]; if (n % 2 > 0) continue; result.push(n * n); } // good const arr = [1, 2, 3, 4]; const isEven = n => n % 2 == 0; const square = n => n * n; const result = arr.filter(isEven).map(square); ``` ### Statelessness Try to keep your functions pure. All functions should ideally produce no side-effects, use no outside data and return new objects instead of mutating existing ones. ```javascript // bad const merge = (target, ...sources) => Object.assign(target, ...sources); merge({ foo: "foo" }, { bar: "bar" }); // => { foo: "foo", bar: "bar" } // good const merge = (...sources) => Object.assign({}, ...sources); merge({ foo: "foo" }, { bar: "bar" }); // => { foo: "foo", bar: "bar" } ``` ### Natives Rely on native methods as much as possible. ```javascript // bad const toArray = obj => [].slice.call(obj); // good const toArray = (() => Array.from ? Array.from : obj => [].slice.call(obj) )(); ``` ### Coercion Embrace implicit coercion when it makes sense. Avoid it otherwise. Don't cargo-cult. ```javascript // bad if (x === undefined || x === null) { ... } // good if (x == undefined) { ... } ``` ### Loops Don't use loops as they force you to use mutable objects. Rely on `array.prototype` methods. ```javascript // bad const sum = arr => { var sum = 0; var i = -1; for (;arr[++i];) { sum += arr[i]; } return sum; }; sum([1, 2, 3]); // => 6 // good const sum = arr => arr.reduce((x, y) => x + y); sum([1, 2, 3]); // => 6 ``` If you can't, or if using `array.prototype` methods is arguably abusive, use recursion. ```javascript // bad const createDivs = howMany => { while (howMany--) { document.body.insertAdjacentHTML("beforeend", ""); } }; createDivs(5); // bad const createDivs = howMany => [...Array(howMany)].forEach(() => document.body.insertAdjacentHTML("beforeend", "") ); createDivs(5); // good const createDivs = howMany => { if (!howMany) return; document.body.insertAdjacentHTML("beforeend", ""); return createDivs(howMany - 1); }; createDivs(5); ``` Here's a [generic loop function](https://gist.github.com/bendc/6cb2db4a44ec30208e86) making recursion easier to use. ### Arguments Forget about the `arguments` object. The rest parameter is always a better option because: 1. it's named, so it gives you a better idea of the arguments the function is expecting 2. it's a real array, which makes it easier to use. ```javascript // bad const sortNumbers = () => Array.prototype.slice.call(arguments).sort(); // good const sortNumbers = (...numbers) => numbers.sort(); ``` ### Apply Forget about `apply()`. Use the spread operator instead. ```javascript const greet = (first, last) => `Hi ${first} ${last}`; const person = ["John", "Doe"]; // bad greet.apply(null, person); // good greet(...person); ``` ### Bind Don't `bind()` when there's a more idiomatic approach. ```javascript // bad ["foo", "bar"].forEach(func.bind(this)); // good ["foo", "bar"].forEach(func, this); ``` ```javascript // bad const person = { first: "John", last: "Doe", greet() { const full = function() { return `${this.first} ${this.last}`; }.bind(this); return `Hello ${full()}`; } } // good const person = { first: "John", last: "Doe", greet() { const full = () => `${this.first} ${this.last}`; return `Hello ${full()}`; } } ``` ### Higher-order functions Avoid nesting functions when you don't have to. ```javascript // bad [1, 2, 3].map(num => String(num)); // good [1, 2, 3].map(String); ``` ### Composition Avoid multiple nested function calls. Use composition instead. ```javascript const plus1 = a => a + 1; const mult2 = a => a * 2; // bad mult2(plus1(5)); // => 12 // good const pipeline = (...funcs) => val => funcs.reduce((a, b) => b(a), val); const addThenMult = pipeline(plus1, mult2); addThenMult(5); // => 12 ``` ### Caching Cache feature tests, large data structures and any expensive operation. ```javascript // bad const contains = (arr, value) => Array.prototype.includes ? arr.includes(value) : arr.some(el => el === value); contains(["foo", "bar"], "baz"); // => false // good const contains = (() => Array.prototype.includes ? (arr, value) => arr.includes(value) : (arr, value) => arr.some(el => el === value) )(); contains(["foo", "bar"], "baz"); // => false ``` ### Variables Favor `const` over `let` and `let` over `var`. ```javascript // bad var me = new Map(); me.set("name", "Ben").set("country", "Belgium"); // good const me = new Map(); me.set("name", "Ben").set("country", "Belgium"); ``` ### Conditions Favor IIFE's and return statements over if, else if, else and switch statements. ```javascript // bad var grade; if (result < 50) grade = "bad"; else if (result < 90) grade = "good"; else grade = "excellent"; // good const grade = (() => { if (result < 50) return "bad"; if (result < 90) return "good"; return "excellent"; })(); ``` ### Object iteration Avoid `for...in` when you can. ```javascript const shared = { foo: "foo" }; const obj = Object.create(shared, { bar: { value: "bar", enumerable: true } }); // bad for (var prop in obj) { if (obj.hasOwnProperty(prop)) console.log(prop); } // good Object.keys(obj).forEach(prop => console.log(prop)); ``` ### Objects as Maps While objects have legitimate use cases, maps are usually a better, more powerful choice. When in doubt, use a `Map`. ```javascript // bad const me = { name: "Ben", age: 30 }; var meSize = Object.keys(me).length; meSize; // => 2 me.country = "Belgium"; meSize++; meSize; // => 3 // good const me = new Map(); me.set("name", "Ben"); me.set("age", 30); me.size; // => 2 me.set("country", "Belgium"); me.size; // => 3 ``` ### Curry Currying is a powerful but foreign paradigm for many developers. Don't abuse it as its appropriate use cases are fairly unusual. ```javascript // bad const sum = a => b => a + b; sum(5)(3); // => 8 // good const sum = (a, b) => a + b; sum(5, 3); // => 8 ``` ### Readability Don't obfuscate the intent of your code by using seemingly smart tricks. ```javascript // bad foo || doSomething(); // good if (!foo) doSomething(); ``` ```javascript // bad void function() { /* IIFE */ }(); // good (function() { /* IIFE */ }()); ``` ```javascript // bad const n = ~~3.14; // good const n = Math.floor(3.14); ``` ### Code reuse Don't be afraid of creating lots of small, highly composable and reusable functions. ```javascript // bad arr[arr.length - 1]; // good const first = arr => arr[0]; const last = arr => first(arr.slice(-1)); last(arr); ``` ```javascript // bad const product = (a, b) => a * b; const triple = n => n * 3; // good const product = (a, b) => a * b; const triple = product.bind(null, 3); ``` ### Dependencies Minimize dependencies. Third-party is code you don't know. Don't load an entire library for just a couple of methods easily replicable: ```javascript // bad var _ = require("underscore"); _.compact(["foo", 0])); _.unique(["foo", "foo"]); _.union(["foo"], ["bar"], ["foo"]); // good const compact = arr => arr.filter(el => el); const unique = arr => [...new Set(arr)]; const union = (...arr) => unique([].concat(...arr)); compact(["foo", 0]); unique(["foo", "foo"]); union(["foo"], ["bar"], ["foo"]); ```