└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # Frontend Guidelines 2 | 3 | ## HTML 4 | 5 | ### Semantics 6 | 7 | HTML5 provides us with lots of semantic elements aimed to describe precisely the content. Make sure you benefit from its rich vocabulary. 8 | 9 | ```html 10 | 11 |
12 |
13 |
14 |

Blog post

15 |

Published: 21st Feb, 2015

16 |
17 |

18 |
19 |
20 | 21 | 22 |
23 |
24 |
25 |

Blog post

26 |

Published:

27 |
28 |

29 |
30 |
31 | ``` 32 | 33 | Make sure you understand the semantics of the elements you're using. It's worse to use a semantic 34 | element in a wrong way than staying neutral. 35 | 36 | ```html 37 | 38 |

39 |
40 | Company 41 |
42 |

43 | 44 | 45 |

46 | Company 47 |

48 | ``` 49 | 50 | ### Brevity 51 | 52 | Keep your code terse. Forget about your old XHTML habits. 53 | 54 | ```html 55 | 56 | 57 | 58 | 59 | 60 | Contact 61 | 62 | 63 | 64 |

Contact me

65 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | Contact 78 | 79 | 80 |

Contact me

81 | 85 | 86 | 87 | ``` 88 | 89 | ### Accessibility 90 | 91 | Accessibility shouldn't be an afterthought. You don't have to be a WCAG expert to improve your 92 | website, you can start immediately by fixing the little things that make a huge difference, such as: 93 | 94 | * learning to use the `alt` attribute properly 95 | * making sure your links and buttons are marked as such (no `
` atrocities) 96 | * not relying exclusively on colors to communicate information 97 | * explicitly labelling form controls 98 | 99 | ```html 100 | 101 |

Logo

102 | 103 | 104 |

Company

105 | ``` 106 | 107 | ### Language & character encoding 108 | 109 | While defining the language is optional, it's recommended to always declare 110 | it on the root element. 111 | 112 | The HTML standard requires that pages use the UTF-8 character encoding. 113 | It has to be declared, and although it can be declared in the Content-Type HTTP header, 114 | it is recommended to always declare it at the document level. 115 | 116 | ```html 117 | 118 | 119 | Hello, world. 120 | 121 | 122 | 123 | 124 | 125 | Hello, world. 126 | 127 | ``` 128 | 129 | ### Performance 130 | 131 | Unless there's a valid reason for loading your scripts before your content, don't block the 132 | rendering of your page. If your style sheet is heavy, isolate the styles that are absolutely 133 | required initially and defer the loading of the secondary declarations in a separate style sheet. 134 | Two HTTP requests is significantly slower than one, but the perception of speed is the most 135 | important factor. 136 | 137 | ```html 138 | 139 | 140 | 141 | 142 | Hello, world. 143 |

...

144 | 145 | 146 | 147 | 148 | Hello, world. 149 |

...

150 | 151 | ``` 152 | 153 | ## CSS 154 | 155 | ### Semicolons 156 | 157 | While the semicolon is technically a separator in CSS, always treat it as a terminator. 158 | 159 | ```css 160 | /* bad */ 161 | div { 162 | color: red 163 | } 164 | 165 | /* good */ 166 | div { 167 | color: red; 168 | } 169 | ``` 170 | 171 | ### Box model 172 | 173 | The box model should ideally be the same for the entire document. A global 174 | `* { box-sizing: border-box; }` is fine, but don't change the default box model 175 | on specific elements if you can avoid it. 176 | 177 | ```css 178 | /* bad */ 179 | div { 180 | width: 100%; 181 | padding: 10px; 182 | box-sizing: border-box; 183 | } 184 | 185 | /* good */ 186 | div { 187 | padding: 10px; 188 | } 189 | ``` 190 | 191 | ### Flow 192 | 193 | Don't change the default behavior of an element if you can avoid it. Keep elements in the 194 | natural document flow as much as you can. For example, removing the white-space below an 195 | image shouldn't make you change its default display: 196 | 197 | ```css 198 | /* bad */ 199 | img { 200 | display: block; 201 | } 202 | 203 | /* good */ 204 | img { 205 | vertical-align: middle; 206 | } 207 | ``` 208 | 209 | Similarly, don't take an element off the flow if you can avoid it. 210 | 211 | ```css 212 | /* bad */ 213 | div { 214 | width: 100px; 215 | position: absolute; 216 | right: 0; 217 | } 218 | 219 | /* good */ 220 | div { 221 | width: 100px; 222 | margin-left: auto; 223 | } 224 | ``` 225 | 226 | ### Positioning 227 | 228 | There are many ways to position elements in CSS. Favor modern layout specifications 229 | such as Flexbox and Grid, and avoid removing elements from the normal document flow, for example 230 | with `position: absolute`. 231 | 232 | ### Selectors 233 | 234 | Minimize selectors tightly coupled to the DOM. Consider adding a class to the elements 235 | you want to match when your selector exceeds 3 structural pseudo-classes, descendant or 236 | sibling combinators. 237 | 238 | ```css 239 | /* bad */ 240 | div:first-of-type :last-child > p ~ * 241 | 242 | /* good */ 243 | div:first-of-type .info 244 | ``` 245 | 246 | Avoid overloading your selectors when you don't need to. 247 | 248 | ```css 249 | /* bad */ 250 | img[src$=svg], ul > li:first-child { 251 | opacity: 0; 252 | } 253 | 254 | /* good */ 255 | [src$=svg], ul > :first-child { 256 | opacity: 0; 257 | } 258 | ``` 259 | 260 | ### Specificity 261 | 262 | Don't make values and selectors hard to override. Minimize the use of `id`'s 263 | and avoid `!important`. 264 | 265 | ```css 266 | /* bad */ 267 | .bar { 268 | color: green !important; 269 | } 270 | .foo { 271 | color: red; 272 | } 273 | 274 | /* good */ 275 | .foo.bar { 276 | color: green; 277 | } 278 | .foo { 279 | color: red; 280 | } 281 | ``` 282 | 283 | ### Overriding 284 | 285 | Overriding styles makes selectors and debugging harder. Avoid it when possible. 286 | 287 | ```css 288 | /* bad */ 289 | li { 290 | visibility: hidden; 291 | } 292 | li:first-child { 293 | visibility: visible; 294 | } 295 | 296 | /* good */ 297 | li + li { 298 | visibility: hidden; 299 | } 300 | ``` 301 | 302 | ### Inheritance 303 | 304 | Don't duplicate style declarations that can be inherited. 305 | 306 | ```css 307 | /* bad */ 308 | div h1, div p { 309 | text-shadow: 0 1px 0 #fff; 310 | } 311 | 312 | /* good */ 313 | div { 314 | text-shadow: 0 1px 0 #fff; 315 | } 316 | ``` 317 | 318 | ### Brevity 319 | 320 | Keep your code terse. Use shorthand properties and avoid using multiple properties when 321 | it's not needed. 322 | 323 | ```css 324 | /* bad */ 325 | div { 326 | transition: all 1s; 327 | top: 50%; 328 | margin-top: -10px; 329 | padding-top: 5px; 330 | padding-right: 10px; 331 | padding-bottom: 20px; 332 | padding-left: 10px; 333 | } 334 | 335 | /* good */ 336 | div { 337 | transition: 1s; 338 | top: calc(50% - 10px); 339 | padding: 5px 10px 20px; 340 | } 341 | ``` 342 | 343 | ### Language 344 | 345 | Prefer English over math. 346 | 347 | ```css 348 | /* bad */ 349 | :nth-child(2n + 1) { 350 | transform: rotate(360deg); 351 | } 352 | 353 | /* good */ 354 | :nth-child(odd) { 355 | transform: rotate(1turn); 356 | } 357 | ``` 358 | 359 | ### Vendor prefixes 360 | 361 | Kill obsolete vendor prefixes aggressively. If you need to use them, insert them before the 362 | standard property. 363 | 364 | ```css 365 | /* bad */ 366 | div { 367 | transform: scale(2); 368 | -webkit-transform: scale(2); 369 | -moz-transform: scale(2); 370 | -ms-transform: scale(2); 371 | transition: 1s; 372 | -webkit-transition: 1s; 373 | -moz-transition: 1s; 374 | -ms-transition: 1s; 375 | } 376 | 377 | /* good */ 378 | div { 379 | -webkit-transform: scale(2); 380 | transform: scale(2); 381 | transition: 1s; 382 | } 383 | ``` 384 | 385 | ### Animations 386 | 387 | Favor transitions over animations. Avoid animating other properties than 388 | `opacity` and `transform`. 389 | 390 | ```css 391 | /* bad */ 392 | div:hover { 393 | animation: move 1s forwards; 394 | } 395 | @keyframes move { 396 | 100% { 397 | margin-left: 100px; 398 | } 399 | } 400 | 401 | /* good */ 402 | div:hover { 403 | transition: 1s; 404 | transform: translateX(100px); 405 | } 406 | ``` 407 | 408 | ### Units 409 | 410 | Use unitless values when you can. Favor `rem` if you use relative units. Prefer seconds over 411 | milliseconds. 412 | 413 | ```css 414 | /* bad */ 415 | div { 416 | margin: 0px; 417 | font-size: .9em; 418 | line-height: 22px; 419 | transition: 500ms; 420 | } 421 | 422 | /* good */ 423 | div { 424 | margin: 0; 425 | font-size: .9rem; 426 | line-height: 1.5; 427 | transition: .5s; 428 | } 429 | ``` 430 | 431 | ### Colors 432 | 433 | If you need transparency, use `rgba`. Otherwise, always use the hexadecimal format. 434 | 435 | ```css 436 | /* bad */ 437 | div { 438 | color: hsl(103, 54%, 43%); 439 | } 440 | 441 | /* good */ 442 | div { 443 | color: #5a3; 444 | } 445 | ``` 446 | 447 | ### Drawing 448 | 449 | Avoid HTTP requests when the resources are easily replicable with CSS. 450 | 451 | ```css 452 | /* bad */ 453 | div::before { 454 | content: url(white-circle.svg); 455 | } 456 | 457 | /* good */ 458 | div::before { 459 | content: ""; 460 | display: block; 461 | width: 20px; 462 | height: 20px; 463 | border-radius: 50%; 464 | background: #fff; 465 | } 466 | ``` 467 | 468 | ### Hacks 469 | 470 | Don't use them. 471 | 472 | ```css 473 | /* bad */ 474 | div { 475 | // position: relative; 476 | transform: translateZ(0); 477 | } 478 | 479 | /* good */ 480 | div { 481 | /* position: relative; */ 482 | will-change: transform; 483 | } 484 | ``` 485 | 486 | ## JavaScript 487 | 488 | ### Performance 489 | 490 | Favor readability, correctness and expressiveness over performance. JavaScript will basically never 491 | be your performance bottleneck. Optimize things like image compression, network access and DOM 492 | reflows instead. If you remember just one guideline from this document, choose this one. 493 | 494 | ```javascript 495 | // bad (albeit way faster) 496 | const arr = [1, 2, 3, 4]; 497 | const len = arr.length; 498 | var i = -1; 499 | var result = []; 500 | while (++i < len) { 501 | var n = arr[i]; 502 | if (n % 2 > 0) continue; 503 | result.push(n * n); 504 | } 505 | 506 | // good 507 | const arr = [1, 2, 3, 4]; 508 | const isEven = n => n % 2 == 0; 509 | const square = n => n * n; 510 | 511 | const result = arr.filter(isEven).map(square); 512 | ``` 513 | 514 | ### Statelessness 515 | 516 | 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. 517 | 518 | ```javascript 519 | // bad 520 | const merge = (target, ...sources) => Object.assign(target, ...sources); 521 | merge({ foo: "foo" }, { bar: "bar" }); // => { foo: "foo", bar: "bar" } 522 | 523 | // good 524 | const merge = (...sources) => Object.assign({}, ...sources); 525 | merge({ foo: "foo" }, { bar: "bar" }); // => { foo: "foo", bar: "bar" } 526 | ``` 527 | 528 | ### Natives 529 | 530 | Rely on native methods as much as possible. 531 | 532 | ```javascript 533 | // bad 534 | const toArray = obj => [].slice.call(obj); 535 | 536 | // good 537 | const toArray = (() => 538 | Array.from ? Array.from : obj => [].slice.call(obj) 539 | )(); 540 | ``` 541 | 542 | ### Coercion 543 | 544 | Embrace implicit coercion when it makes sense. Avoid it otherwise. Don't cargo-cult. 545 | 546 | ```javascript 547 | // bad 548 | if (x === undefined || x === null) { ... } 549 | 550 | // good 551 | if (x == undefined) { ... } 552 | ``` 553 | 554 | ### Loops 555 | 556 | Don't use loops as they force you to use mutable objects. Rely on `array.prototype` methods. 557 | 558 | ```javascript 559 | // bad 560 | const sum = arr => { 561 | var sum = 0; 562 | var i = -1; 563 | for (;arr[++i];) { 564 | sum += arr[i]; 565 | } 566 | return sum; 567 | }; 568 | 569 | sum([1, 2, 3]); // => 6 570 | 571 | // good 572 | const sum = arr => 573 | arr.reduce((x, y) => x + y); 574 | 575 | sum([1, 2, 3]); // => 6 576 | ``` 577 | If you can't, or if using `array.prototype` methods is arguably abusive, use recursion. 578 | 579 | ```javascript 580 | // bad 581 | const createDivs = howMany => { 582 | while (howMany--) { 583 | document.body.insertAdjacentHTML("beforeend", "
"); 584 | } 585 | }; 586 | createDivs(5); 587 | 588 | // bad 589 | const createDivs = howMany => 590 | [...Array(howMany)].forEach(() => 591 | document.body.insertAdjacentHTML("beforeend", "
") 592 | ); 593 | createDivs(5); 594 | 595 | // good 596 | const createDivs = howMany => { 597 | if (!howMany) return; 598 | document.body.insertAdjacentHTML("beforeend", "
"); 599 | return createDivs(howMany - 1); 600 | }; 601 | createDivs(5); 602 | ``` 603 | 604 | Here's a [generic loop function](https://gist.github.com/bendc/6cb2db4a44ec30208e86) making recursion easier to use. 605 | 606 | ### Arguments 607 | 608 | Forget about the `arguments` object. The rest parameter is always a better option because: 609 | 610 | 1. it's named, so it gives you a better idea of the arguments the function is expecting 611 | 2. it's a real array, which makes it easier to use. 612 | 613 | ```javascript 614 | // bad 615 | const sortNumbers = () => 616 | Array.prototype.slice.call(arguments).sort(); 617 | 618 | // good 619 | const sortNumbers = (...numbers) => numbers.sort(); 620 | ``` 621 | 622 | ### Apply 623 | 624 | Forget about `apply()`. Use the spread operator instead. 625 | 626 | ```javascript 627 | const greet = (first, last) => `Hi ${first} ${last}`; 628 | const person = ["John", "Doe"]; 629 | 630 | // bad 631 | greet.apply(null, person); 632 | 633 | // good 634 | greet(...person); 635 | ``` 636 | 637 | ### Bind 638 | 639 | Don't `bind()` when there's a more idiomatic approach. 640 | 641 | ```javascript 642 | // bad 643 | ["foo", "bar"].forEach(func.bind(this)); 644 | 645 | // good 646 | ["foo", "bar"].forEach(func, this); 647 | ``` 648 | ```javascript 649 | // bad 650 | const person = { 651 | first: "John", 652 | last: "Doe", 653 | greet() { 654 | const full = function() { 655 | return `${this.first} ${this.last}`; 656 | }.bind(this); 657 | return `Hello ${full()}`; 658 | } 659 | } 660 | 661 | // good 662 | const person = { 663 | first: "John", 664 | last: "Doe", 665 | greet() { 666 | const full = () => `${this.first} ${this.last}`; 667 | return `Hello ${full()}`; 668 | } 669 | } 670 | ``` 671 | 672 | ### Higher-order functions 673 | 674 | Avoid nesting functions when you don't have to. 675 | 676 | ```javascript 677 | // bad 678 | [1, 2, 3].map(num => String(num)); 679 | 680 | // good 681 | [1, 2, 3].map(String); 682 | ``` 683 | 684 | ### Composition 685 | 686 | Avoid multiple nested function calls. Use composition instead. 687 | 688 | ```javascript 689 | const plus1 = a => a + 1; 690 | const mult2 = a => a * 2; 691 | 692 | // bad 693 | mult2(plus1(5)); // => 12 694 | 695 | // good 696 | const pipeline = (...funcs) => val => funcs.reduce((a, b) => b(a), val); 697 | const addThenMult = pipeline(plus1, mult2); 698 | addThenMult(5); // => 12 699 | ``` 700 | 701 | ### Caching 702 | 703 | Cache feature tests, large data structures and any expensive operation. 704 | 705 | ```javascript 706 | // bad 707 | const contains = (arr, value) => 708 | Array.prototype.includes 709 | ? arr.includes(value) 710 | : arr.some(el => el === value); 711 | contains(["foo", "bar"], "baz"); // => false 712 | 713 | // good 714 | const contains = (() => 715 | Array.prototype.includes 716 | ? (arr, value) => arr.includes(value) 717 | : (arr, value) => arr.some(el => el === value) 718 | )(); 719 | contains(["foo", "bar"], "baz"); // => false 720 | ``` 721 | 722 | ### Variables 723 | 724 | Favor `const` over `let` and `let` over `var`. 725 | 726 | ```javascript 727 | // bad 728 | var me = new Map(); 729 | me.set("name", "Ben").set("country", "Belgium"); 730 | 731 | // good 732 | const me = new Map(); 733 | me.set("name", "Ben").set("country", "Belgium"); 734 | ``` 735 | 736 | ### Conditions 737 | 738 | Favor IIFE's and return statements over if, else if, else and switch statements. 739 | 740 | ```javascript 741 | // bad 742 | var grade; 743 | if (result < 50) 744 | grade = "bad"; 745 | else if (result < 90) 746 | grade = "good"; 747 | else 748 | grade = "excellent"; 749 | 750 | // good 751 | const grade = (() => { 752 | if (result < 50) 753 | return "bad"; 754 | if (result < 90) 755 | return "good"; 756 | return "excellent"; 757 | })(); 758 | ``` 759 | 760 | ### Object iteration 761 | 762 | Avoid `for...in` when you can. 763 | 764 | ```javascript 765 | const shared = { foo: "foo" }; 766 | const obj = Object.create(shared, { 767 | bar: { 768 | value: "bar", 769 | enumerable: true 770 | } 771 | }); 772 | 773 | // bad 774 | for (var prop in obj) { 775 | if (obj.hasOwnProperty(prop)) 776 | console.log(prop); 777 | } 778 | 779 | // good 780 | Object.keys(obj).forEach(prop => console.log(prop)); 781 | ``` 782 | 783 | ### Objects as Maps 784 | 785 | While objects have legitimate use cases, maps are usually a better, more powerful choice. When in 786 | doubt, use a `Map`. 787 | 788 | ```javascript 789 | // bad 790 | const me = { 791 | name: "Ben", 792 | age: 30 793 | }; 794 | var meSize = Object.keys(me).length; 795 | meSize; // => 2 796 | me.country = "Belgium"; 797 | meSize++; 798 | meSize; // => 3 799 | 800 | // good 801 | const me = new Map(); 802 | me.set("name", "Ben"); 803 | me.set("age", 30); 804 | me.size; // => 2 805 | me.set("country", "Belgium"); 806 | me.size; // => 3 807 | ``` 808 | 809 | ### Curry 810 | 811 | Currying is a powerful but foreign paradigm for many developers. Don't abuse it as its appropriate 812 | use cases are fairly unusual. 813 | 814 | ```javascript 815 | // bad 816 | const sum = a => b => a + b; 817 | sum(5)(3); // => 8 818 | 819 | // good 820 | const sum = (a, b) => a + b; 821 | sum(5, 3); // => 8 822 | ``` 823 | 824 | ### Readability 825 | 826 | Don't obfuscate the intent of your code by using seemingly smart tricks. 827 | 828 | ```javascript 829 | // bad 830 | foo || doSomething(); 831 | 832 | // good 833 | if (!foo) doSomething(); 834 | ``` 835 | ```javascript 836 | // bad 837 | void function() { /* IIFE */ }(); 838 | 839 | // good 840 | (function() { /* IIFE */ }()); 841 | ``` 842 | ```javascript 843 | // bad 844 | const n = ~~3.14; 845 | 846 | // good 847 | const n = Math.floor(3.14); 848 | ``` 849 | 850 | ### Code reuse 851 | 852 | Don't be afraid of creating lots of small, highly composable and reusable functions. 853 | 854 | ```javascript 855 | // bad 856 | arr[arr.length - 1]; 857 | 858 | // good 859 | const first = arr => arr[0]; 860 | const last = arr => first(arr.slice(-1)); 861 | last(arr); 862 | ``` 863 | ```javascript 864 | // bad 865 | const product = (a, b) => a * b; 866 | const triple = n => n * 3; 867 | 868 | // good 869 | const product = (a, b) => a * b; 870 | const triple = product.bind(null, 3); 871 | ``` 872 | 873 | ### Dependencies 874 | 875 | Minimize dependencies. Third-party is code you don't know. Don't load an entire library for just a couple of methods easily replicable: 876 | 877 | ```javascript 878 | // bad 879 | var _ = require("underscore"); 880 | _.compact(["foo", 0])); 881 | _.unique(["foo", "foo"]); 882 | _.union(["foo"], ["bar"], ["foo"]); 883 | 884 | // good 885 | const compact = arr => arr.filter(el => el); 886 | const unique = arr => [...new Set(arr)]; 887 | const union = (...arr) => unique([].concat(...arr)); 888 | 889 | compact(["foo", 0]); 890 | unique(["foo", "foo"]); 891 | union(["foo"], ["bar"], ["foo"]); 892 | ``` 893 | --------------------------------------------------------------------------------