FlixTech guidelines and best practices on creating a maintainable and consistent frontend

This is a collection of loosely coupled rules that should help all developers with certain decisions regarding our code and behaviour. The goal of the document is to align all the independent teams among the same frontend development best practices and rules. This simplifies the SCS integration and allows us to avoid some of the potential problems and conflicts and makes frontend consistent among the company.

Version 0.1.1

Authors Team Hive

Table of Contents

General codestyle rules

At a very high-level, we want:

  • two (2) space indents, no tabs;
  • multi-line CSS;
  • meaningful use of whitespace;
  • good code documenting culture;
  • a new line for every block, list, or table element, and indent every such child element.

HTML

Codestyle

Use meaningful whitespace in your HTML

Separate independent but loosely related snippets of markup with a single empty line, for example:

<ul class="primary-nav">

  <li class="primary-nav__item">
    <a class="primary-nav__link" href="/">Home</a>
  </li>

  <li class="primary-nav__item primary-nav__trigger">
    <a class="primary-nav__link" href="/about">About</a>

    <ul class="primary-nav__sub-nav">
      <li><a class="hcr-link-10-8-1" href="/about/products">Products</a></li>
      <li><a class="hcr-link-10-8-1" href="/about/company">Company</a></li>
    </ul>

  </li>

  <li class="primary-nav__item">
    <a class="primary-nav__link" href="/contact">Contact</a>
  </li>

</ul>

Multimedia Fallback

Provide alternative contents for multimedia.


<img src="spreadsheet.png"/>

<img src="spreadsheet.png" alt="Spreadsheet screenshot"/>

Type Attributes

Do not use type attributes for style sheets (unless not using CSS) and scripts (unless not using JavaScript). Specifying type attributes in these contexts is not necessary as HTML5 implies text/css and text/javascript as defaults. This can be safely done even for older browsers.


<link rel="stylesheet" href="//www.google.com/css/maia.css" type="text/css"/>

<link rel="stylesheet" href="//www.google.com/css/maia.css"/>

Protocol

Omit the protocol from embedded resources. Omit the protocol portion (http:, https:) from URLs pointing to images and other media files, style sheets, and scripts unless the respective files are not available over both protocols. Omitting the protocol—which makes the URL relative—prevents mixed content issues and results in minor file size savings.


<script src="https://www.url.com/js/gweb/analytics/autotrack.js"></script>

.example {
 background: url(https://www.url.com/images/example);
}

<script src="//www.url.com/js/gweb/analytics/autotrack.js"></script>

.example {
 background: url(//www.url.com/images/example);
}

CSS

Codestyle

General rules

  • When grouping selectors, keep individual selectors to a single line.
  • Include one space before the opening brace of declaration blocks for legibility.
  • Place closing braces of declaration blocks on a new line.
  • Include one space after : for each declaration.
  • Each declaration should appear on its own line for more accurate error reporting.
  • End all declarations with a semi-colon.
  • Comma-separated property values should include a space after each comma (e.g., box-shadow).
  • Lowercase all hex values, e.g., #fff. Lowercase letters are much easier to discern when scanning a document as they tend to have more unique shapes.
  • Use shorthand hex values where available, e.g., #fff instead of #ffffff.
  • Quote attribute values in selectors, e.g., input[type="text"].
  • Avoid specifying units for zero values, e.g., margin: 0; instead of margin: 0px;.

Declaration order

Related property declarations should be grouped together following the order:

  1. Positioning
  2. Box model
  3. Typographic
  4. Visual
.declaration-order {
  /* Positioning */
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 100;

  /* Box-model */
  display: block;
  float: right;
  width: 100px;
  height: 100px;

  /* Typography */
  font: normal 13px "Helvetica Neue", sans-serif;
  line-height: 1.5;
  color: #333;
  text-align: center;

  /* Visual */
  background-color: #f5f5f5;
  border: 1px solid #e5e5e5;
  border-radius: 3px;

  /* Misc */
  opacity: 1;
}

Comments

Begin every new major section of a CSS project with a title. Ideally, any CSS ruleset should be preceded by a C-style comment explaining the point of the CSS block.

/* #HEADER
------------------------------------*/

/*
* modifier class, fixes site header block on top of the page
*/

.site-header--fixed {
  position: fixed;
  top: 0;
}

For SASS/SCSS specific stuff use double slashes so those comments will be omitted in resulting file.

Do not use CSS-style multiline comments within the scss/sass files


// COLOR VARIABLES
// -----------------------------------

// Main brand colors

$primary-color: #73d700;
$secondary-color: #ffad00;

CSS selectors

At a very high-level, we want:

  • Class names as CSS selectors;
  • We are utilizing BEM like naming for CSS class names;
  • Avoid nesting in CSS selectors;
  • JavaScript hooks (avoid binding to the same class in both your CSS and JavaScript).

Class names

Use only class names as CSS selectors This allows us to maintain consistency in our code, avoid specificity problems, separate HTML markup and style declarations. Omit using ID's or tag names in CSS. While it is possible to select elements by ID in CSS, it should generally be considered an anti-pattern. ID selectors introduce an unnecessarily high level of specificity to your rule declarations, and they are not reusable. Let's preserve one importance order through all our CSS files so you won't need to write any nasty hacks like "!important".

/* Not recommended */
a {
  color: #0f0;
}

/* Recommended */
.link {
  color: #0f0;
}

BEM-like naming

For larger, more interrelated pieces of UI that require a number of classes, we use a BEM-like naming convention ([http://getbem.com/naming/]).

BEM, meaning Block, Element, Modifier, is a front-end methodology coined by developers working at Yandex. Whilst BEM is a complete methodology, here we are only concerned with its naming convention. Further, the naming convention here only is BEM-like; the principles are exactly the same, but the actual syntax differs slightly.

BEM splits components’ classes into three groups:

  • Block: The sole root of the component.
  • Element: A component part of the Block.
  • Modifier: A variant or extension of the Block.
<form class="form form--inverted">
 <input class="form__input" type="text"/>
 <input class="form__submit form__submit--disabled" type="submit"/>
</form>

Here "form" is a block, "input" and "submit" are elements and "inverted" and "disabled" are the modifiers. This results in a simple flat CSS structure:

.form { }
.form--inverted { }
.form__input { }
.form__submit { }
.form__submit--disabled { }

It is important to know when BEM scope starts and stops. As a rule, BEM applies to self-contained, discrete parts of the UI.

Nesting is nasty

Try to keep CSS structure as flat as possible avoiding nested rules. Here are a couple of reasons why we want that:

  • nesting increases specificity (which should always be well managed);
  • nesting introduces location dependency (a hallmark of an inflexible system)
  • nesting reduces portability (meaning we can’t move things around if we need to);
  • nesting increases fragility (nesting means more chances for the selector to go wrong).

In short, keep your CSS selectors short.

/* Not recommended */
.nav .link {
  color: #fff;
}

/* Recommended, BEM-like flat structure */
.nav__link {
  color: #fff;
}

Javascript

JS selectors

Separate js classes

Use separate "js-" prefixed classes for javascript event binding or any other javascript related thing. Namespace separation allows us to clearly see and understand which parts of HTML are used in JS somewhere and separate javascript bindings from layout. Conflating the two often leads to, at a minimum, time wasted during refactoring when a developer must cross-reference each class they are changing, and at its worst, developers being afraid to make changes for fear of breaking functionality.

<form class="payment-form js-payment-form">
   <input type="radio" class="payment-method js-payment-method"/>
   <input type="radio" class="payment-method js-payment-method"/>
</form>

Here we have a separate "js-payment-form" class which is used to initialize our jquery widget and a separate js-prefixed classes ("js-payment-method") to use within the widget to find the controls. So when we want to change the HTML we clearly see which elements are used in javascript and preserve the same behaviour in the new markup. This makes refactoring and code support much easier.

$.fn.checkoutFormView = function (options) {
  var settings = {
    //payment methods controls
    methods: '.js-payment-method'
  };
};

$("js-payment-form").checkoutFormView();