Frontend coding style

Shortcuts:

HTML

Common

  • Inline style is evil

    <p style="color: red;">
    Inline style cannot be cached.<br />
    Inline style is difficult to overwrite.<br />
    Inline style makes HTML less readable.<br />
    Inline style is harder to spot.<br />
    </p>
    
  • Inline script is evil (except Google Analytics)

    <script>
        console.log('Inline script cannot be cached.');
        console.log('Inline script makes HTML less readable.');
        console.log('Inline script blocks loading of page.');
    </script>
    
  • Style your HTML, don’t HTML your style (avoid adding divs for style)

    <div class="wrapper">
        <div class="inner">
            <div class="content">
                <p class="text>
                    All these tags have no acutual meaning.<br />
                    Consider HTML as data model, it should represent data, not style placeholders.<br />
                    Good practice is to write you HTML first, based on the structure of the content, then style.<br />
                    It's almost never needed to add more tags, have you tried :before and :after yet?<br />
                </p>
            </div>
        </div>
    </div>
    
  • Empty newline at the end of the file.

Indentation

  • Indent with 4 spaces

    <html>
        <body>
        </body>
    </html>
    
  • Indent HTML and template tags. (except {% block %} on root level).

    {% block content %}
    <article>
        {% if show_header %}
            {% block article__header %}
                <header>
                </header>
            {% endblock article__header %}
        {% endif %}
    </article>
    {% endblock content %}
    

Data-attributes

  • (Meta)data should be stored in data- attributes.

    <article data-article-id="1">...</article>
    
  • Variables should be passed using data-attributes as well. They are no excuse for inline script.

    <article data-some-variable="1">...</article>
    

Elements

  • Avoid the id attribute, unless there’s a good reason.

    <article id="article-1" />  <!-- wrong -->
    <article class="article" data-id="1" /> <!-- better -->
    
    <!-- ok, since it's useful in unit tests with WebTest -->
    <form id="submit-article">...</form>
    
  • Use semantic tags like <main>, <nav>, <article>, <section>, <aside>, <footer> instead of meaningless <div> s.

    <!DOCTYPE html>
    <html>
        <head></head>
        <body>
            <main>
                <nav>
                </nav>
    
                <article>
                    <header></header>
                    <section></section>
                    <section></section>
                    <footer></footer>
                </article>
    
                <footer>
                </footer>
            </main>
        </body>
    </html>
    

Sass

Common

  • Readability comes first

  • Annotate when useful

Globals

  • Avoid global styling - leave that to the CSS reset.

  • Limit global configuration to: - Grid - Breakpoints - Colors - Font definitions

Indentation

  • Indent using two spaces

    .block {
      width: 100%;
    }
    

Nesting

  • Namespace BEM blocks

    .block {
      // Everything should be nested inside .block
      // This makes sure no elements "bleed" to the global scope
      .block__element {
        ...
      }
    }
    
  • Nest maximum 3 levels deep

    .block {             // One
      .block__element {  // Two
        &:hover {        // Three
          color: #0000FF;
        }
      }
    }
    

Newlines

  • 1 empty newline after mixin/variable block

    .block__element-one {
    }
    
    .block__element-two {
    }
    
  • Empty newline at the end of the file.

Order

  • Block modifiers come before block elements, element modifier come after the element. Example:

    .block {  // .block is the basic element
      // --active is the modifier for .block, and should be grouped with .block
      &.block--active {
      }
    
      // __element is a child element dependent on .block
      .block__element {
      }
    
      // --disabled is the modifier for .block__element, and should be grouped with .block__element
      .block__element--disabled {
      }
    }
    
  • Mixins always come first, and then group attributes logically.

    Mixins come first so that their behaviour can still be overridden. Logical groups are for example text styling and borders.

    .block {
      @include span-columns(4 of 12);
    
      font-size: 18px;
      color: #FFF;
    
      border: solid 1px #FFFF00;
      border-radius: 5px;
    }
    

Selectors

  • Use BEM class naming.

    // BEM (Block, Element, Modifier) is a structured naming convention for CSS classes
    // A double underscore (__) separates the element from a block
    // A double dash (--) separates the modifier from the block or element
    // These fixed patters make it also possible to be parsed by (JavaScript) code
    
    .block {  // A block describes a standalone component
      &.block--modifier {  // A modifier describes a state or theme for eithe a block or an element
      }
    
      .block__element {  // An element is a component that depends on a block
      }
    
      .block__element--modifier {  // This modifier desrcibes the state or theme for an element
      }
    }
    
  • Maximum one BEM block per file

    // file src/bptl/sass/components/blocks/_block.scss
    
    .block {  // That's it, no more blocks in this file
      // ...
    }
    
  • Only select using (BEM) class names (.block__element), not using tag/id.

    div {  // Bad, tags may change an that would break our code
    }
    
    article { // Also bad, event semantic (descriptive) tags may change
    }
    
    h1 {  // Also bad, a marketeer may drop in and ask you to change it into an h2 (design will break and designer will be mad)
    }
    
    #content {  // Bad, we can't repeat this anymore because id's must be unique
    }
    
    .content {  // Better, content is our block
      .content__heading {  // Better, content__heading is a valid class name for an h1, or h2 in block content
      }
      .content__body {  // This could be a class name for a paragraph in block content
      }
    }
    
    .wysiwyg-content {
      h1 {  // Necessity breaks rule - WYSIWYG editors don't adhere to BEM.
      }
    }
    

Variables

Privatize variables by assigning them on top of the module.

$article-color: $color;  // We copy the contents of a global variable into a private one
$article-font: $font;    // This allow us easily "fix" the values and reuse our component

.article {
  color: $article-color;  // We use private values here
  font-family: $article-font;
}

JavaScript

Common

  • Readability first

  • Annotate when useful - e.g. input for functions/methods and return values/types.

    /**
     * Helper method to add an additional class name with a specific modifier (--modifier) to a BEM (Block Element Modifier) element
     * A modifier class is created for each of the existing class names
     * Class names containing "--" (modifier pattern) are discarded
     * Double class names are prevented
     * @param {HTMLElement} node The block/element to append the class name to (block, block__element)
     * @param {String} modifier The name of the modifier (--name)
     */
    function addModifier(node, modifier) {
    }
    

Indentation

  • Indent using 4 spaces

Classes

  • Use TitledCamelCase for class names

    class Header {  // Bonus points: match class to BEM block name
    }
    

Conditionals

  • Put a space between the operator and brackets

    if (foo === 'bar') {
        // ...
    }
    

Constants

  • Use the const keyword

  • Use UPPERCASE

  • Put constants at the top of the module, below the imports

    import {Foo} from 'bar.js';
    
    const MY_AWESOME_CONSTANT = 'foo';
    

Event binding

  • Separate wiring events with event handlers from logic

    class Handler {
    
      /**
       * We separate "wiring" from the main logic so we can resure the logic
       */
      setUpOpen() {
          BUTTON_OPEN.addEventListener('click', this.open.bind(this));
      }
    
      /**
       * We can now reuse `this`
       */
      open(event) {
          // `this` points to the `handler` instance
      }
    
    }
    

Functions

  • use camelCase names

  • no space between function and brackets

  • opening bracket goes on the same line, closing bracket has its own line

Example:

function fooBar(arg1, arg2) {
    // ...
}

Line breaks/newlines

  • watch the line length: soft limit on 79 characters, hard limit on 119

  • no newline inside logical block:

    function doFooBar() {
    
        // ^ Bad, keep related code together
        console.log('indent', 4, 'spaces');
    }
    
  • Empty newline after method/variable block.

    function doFooBar() {
        let fooBar = 'foobar';
    
        console.log(fooBar);
    }
    
  • 2 empty lines after top level function/class/block

    const FOO = 'foo';
    const BAR = 'bar';
    
    
    function doFooBaz() {  // 2 Empty newlines after a block of constants
        console.log('foobaz');
    }
    
    
    class Foo {  // 2 Empty newlines after a top level function
        constructor() {
            super();
            this.doBar();
        }
    
        doBar() {  // 1 Empty newline after method
            let bar = new Bar();
        }
    }
    
    
    class Bar {   // 2 Empty newlines after a class
        constructor() {
            super();
            this.doBar();
        }
    
        doBar() {
            let bar = new Bar();
        }
    }
    
  • Empty newline at the end of the file

Variables

  • Use the let keyword instead of var

  • Group variable declarations together

  • Use camelCase names

Example:

function doFooBar() {
    let foo = 'foo',
        bar = 'bar',
        fooBar = foo+bar;

    console.log(fooBar);
}

Tests

  • Name the test files foo.spec.js. .spec indicates that it’s a test file