Home

Awesome

Front End Coding Guidelines

Overview

This document contains guidelines for web applications built by WHS. It is available to anyone who wishes to check or contribute our best practices.

This document's primary motivation is to promote code consistency. By maintaining consistency in coding styles and conventions, we can ease the burden of legacy code maintenance, and mitigate risk of breakage in the future. By adhering to best practices, we ensure a consistent user experience, optimized page loading, and maintainable code.

This document outlines the conventions we use at WHS. This is an open document and everything is up for discussion/consideration. We have a tremendous amount of legacy code that does not adhere to these rules, and if you find yourself in one of those files it is in everyone's best interest to refactor it.

These guidelines are not specific to WHS. Our product is not unique enough to warrant deviations from industry standards. Much of this document has been adapted from other front end development guidelines. We're not developing on a separate internet or for a unique browser, so we shouldn't need to invent new standards.

Supported Browsers

Supported Browser List

We do not guarantee that all functionality will work the same between browsers. In fact, with responsive design, we intentionally change layout and style for different browsers. Users need to be able to accomplish the same tasks with all of our supported browsers, but the mechanisms are free to change based on browser features.

HTML

Markup defines the structure and outline of a document. Markup is not intended to define the look and feel of the content on the page beyond rudimentary concepts such as headers, paragraphs, and lists. The presentation attributes of HTML have all been deprecated and style should be contained in style sheets.

HTML5

HTML5 is a new version of HTML and XHTML. The HTML5 draft specification defines a single language that can be written in HTML and XML. It attempts to solve issues found in previous iterations of HTML and addresses the needs of web applications, an area previously not adequately covered by HTML4. source.

We use the HTML5 Doctype and will use HTML5 features when appropriate.

Use Attribute Selectors

Attribute Selectors are magic and can be used to target elements without adding a class or ID.

// target elements on a page that link to pdf documents
<style>
    a[href$=".pdf"] {
        padding-right: 20px;
        background: url(magic-pdf-icon.png) no-repeat left center;
    }
</style>

<a title="Important pdf" href="important.pdf">Important pdf</a>

General Markup Guidelines

The following are general guidelines for structuring your HTML markup. Authors are reminded to always use markup which represents the semantics of the content in the document being created.

<table>
    <thead>
        <tr>
            <th>Table header 0</th>
            <th>Table header 1</th>
            <th>Table header 2</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <th scope="row">Row header</th>
            <td>Table data 1</td>
            <td>Table data 2</td>
        </tr>
    </tbody>
</table>

Quoting Attributes

The HTML5 specification defines quotes around attributes values as optional. For consistency with attributes that accept whitespace, all attributes values should be quoted.

<p class="line note" data-attribute="106">This is my paragraph of special text.</p>

CSS

The second component of a web page is the presentation information contained in the Cascading Style Sheet (CSS). Web browsers successful implementation of CSS has given web authors site-wide control over the look and feel of their web sites.

Just as the information on a web page is semantically described in the HTML Markup, CSS describes all presentation aspects of the page via a description of its visual properties. CSS is powerful in that these properties are mixed and matched via identifiers to control the page's layout and visual characteristics through the layering of style rules (the "cascade").

We use the .LESS CSS preprocessor to aid our CSS generation. Care should be taken when writing css .LESS makes it easy to nest selectors that compile into long selectors. Long selectors chains can cause file bloat, selector precedence problems, adversely affect problems and make css harder to maintain and edit.

Rule of thumb

!important is a sign that we have a selector war and other selectors have too high of specificity, try to refactor the css instead of adding !important.

Get to know CSS Selectors.

Embrace CSS3 to progressively enhance styles for newer browsers.

We aren't all CSS experts, reach out to one of our many knowledgable CSS devs before hacking together a solution in JavaScript.

Organization

You should include exactly one stylesheet on your page and no more. This base file should then import all dependancies and components. Components and styles for different sections should be contained in their own stylesheet and should not cross-pollinate. The home page styles directory, for example, contains the files

styles
|-- homepage.less
|-- _layout.less
|-- _featuredNews.less
|-- _featuredVideo.less
|-- _promoWeblets.less

homepage.less is included on the page and imports the different files for each section like this:

@import '/themes/common/css/minimal';
@import 'layout';
@import 'featuredNews';
@import 'featuredVideo';
@import 'promoWeblets';

And that's it. minimal.less and the legacy-filled theme_builder.less import site chrome like the header, footer, nav, and other junk you don't have to worry about unless you're Obelisk. One of these files must be included. The master themes_common.less and a sponsor's theme.less are magically included in every stylesheet by default and import our Pattern Library components, so you can utilize our global variables and mixins in any LESS file on the site.

Pattern Library

Our Pattern Library is broken up into three pieces, core, components, and modules.

Core contains base styles for our site's body, header, footer, and navigation. It also handles a lot of the global responsive bits. Anything between the nav and the footer should not use anything from core.

Components describe define things like forms, buttons, tables, sprites, grids, etc, that are used frequently across the site. All of these components are organized into mixins and included for you so you can call them at any time, but some have special instructions. Check the documentation.

Modules are more specialized components like modals, tab panels, and progress bars. Since these components are used infrequently and tend to be more customizable they live here.

There is one more piece of the Pattern Library that contains legacy/deprecated components, and you should stay away from it unless you're Obelisk.

BEM, OOCSS

BEM is an object-oriented methodology for organizing resuable blocks of mark-up and CSS. Many of our pattern library components are being ported to BEM. It allows us to rapidly prototype and redesign components because they are not coupled to each other.

.block {}
.block__element {}
.block--modifier {}

BEM is very easy to wrap your head around, and has helped reduce CSS bloat casued by nesting, overrides, and repetitive mixins. We reccomend you adopt this convention when building applications. Get to know BEM syntax.

Theme Variables

Use common variables for things like colors and gutters. This way clients can override variables with their branding colors and fonts if need be. All global variables can all be located in themes_common.less, but the ones we recommend using are in the Pattern Library.

Here's a good example of how to use theme variables wisely.

.class {
    color: @brand_Primary;
    background: @gray_Lighter;
    border: 1px solid @gray_Light;
}

CSS Code Formatting

We want to make sure our LESS looks familiar to everyone editing it. That's why these guidelines are not simply suggestions, but the de facto style you should be coding. The Front-End Community of Practice decides on the style based on our standards. Don't get busted by the COPs.

Indentation

Indent four spaces, no tabs

Selectors

Class and ID names should be camelCase

.className {...}
#theIdName {...}

HTML elements should be lowercase, of course. That said, avoid styling naked tags.

div {...}
span {...}

Put a space between selector and opening bracket and put the closing bracket on a separate, non-indented line

.class {
    color: #000;
}

Return after each closing bracket

.class1 {
    color: #000;
}

.class1 {
    color: #000;
}

Put multiple selectors on separate lines, separated by commas

.class1,
.class2,
.class3,
.class4 {
    color: #000;
}

Properties

Make sure there is a space between property and value, each property is indented one level, and end each line with a semi-colon ;

div {
    color: #000;
    padding: 16px;
    height: 100px;
}

Specify units (px, em, %) unless it is 0 (then omit the units).

padding: 10px 0 15px;

Specify HEX values for colors

#000
#FF0138

Use LESS functions to adjust hue, saturation, lightness, or transparency

color: fade(#000, 50%);
background: mix(#FFF, #000, 50%);

Strings must use single-quotes

a:after {
    content: 'pizza';
}

CSS3 properties that support layering should be on separate lines, with the semi-colon on the last line

.class {
    box-shadow:
        0 2px 4px #000,
        0 4px 8px #333;
    color: #000;
}

Variables

In LESS, variables are declared with @ preceding the name.

@themeColor: #F00;

Any color or layout value used more than once should be a variable

@alertColor: #0F0;
@columnWidth: 80px;

Any variable that is available for client customization must be declared globally in themes_common

Mixins

Always add parenthesis to declare mixins. Mixins should be styled like selectors. Do not put a space after the mixin name.

.mixin() {
    color: #F00;
}

Include mixins first when declaring mixins inside selectors

div {
    .mixin();
    padding: 16px;
}

Define mixins at the bottom of the document or in their own separate file via @import

div {
    .mixin();
}
...
.mixin() {
    color: #F00;
}

Do not put spaces around parameters

.mixin(@color) {
    background: @color;
}

Specify default parameters whenever possible.

.mixin(@color: #F00) {
    background: @color;
}

When calling mixins, you do not need to specify the parameter name, just the value

.class {
    .mixin(#F00, 100px);
}

Multiple parameters with values should be separated on their own lines.

.mixin(
    @color: #F00,
    @width: 100px
    ) {
    background: @color;
    width: @width;
}

Guarded mixins with boolean values that are true do not need = true

.mixin(@boolean) when (@boolean) {...}

Use the when not keyword when specifying falsey booleans

.mixin(@boolean) when not (@boolean) {...}

Use bundles to contain similar mixins

#gradient {
    .linear() {...}
    .radial() {...}
}

Call the bundles with the greater than > character

div {
    #gradient > .linear(...);
}

Make sure you're not redefining mixins that exist globally! Check components > common.less

Stay Rad

Javascript

JavaScript can be added to augment existing functionality on a page. JavaScript is a last resort. Do not use JavaScript to style the page.

Do not rely on JavaScript to render the initial page contents. Read about Twitter's dropping hash urls

Formatting

Indentation

Indent with a tab, do not mix spaces and tabs.

Declaring variables

Declare variables at the beginning of their scope in a single statement. Declaring variables in a single statement performs faster than spreading them around, minifies better, makes them easier to find, and reduces hoisting errors.

(function (){
    'use strict';
    // declare module wide variables at the beginning of module scope in a single statement
    var moduleA = 4,
        moduleB = 5;

    function aFunc (msg) {
        // declare local variables at the beginning of function scope
        var standardGreeting = 'Hello';

        if (!!msg) {
            return standardGreeting + ' A: ' + moduleA;
        } else {
            return msg + 'B: ' + moduleB;
        }
    }

    alert(aFunc()); // Hello A: 4
    alert(aFunc('ohai!')); // ohai! B: 5
})();

Statement Termination

Always include semi-colons, don't rely on Javscript's automatic semi-colon insertion rules.

Line Length

Limit line length to 80 characters, for lines greater than 80 characters break after an operator and double indent the next line unless doing an assignment.

// double indent wrapped lines
if (isLongPoorlyNamedFunctionReturningBoolean() && !isGiraffe() && !isFishy() ||
        isBriney() && isFishy()) {
    fooWithFeeling();
}

// exception: when doing assignment
var UI_MESSAGE = stringBegin + option[1] + option[3] + stringMiddle +
                 stringEnder;

Naming

Use camel case for variables and regular functions, pascal case for constructors and all caps to indicate a constant. Variables and constructors should begin with a noun and regular functions should begin with a verb.

function MoneyMaker(amount) {
    var UI_UNIT = 'dollars';

    this.money = amount || 1;
    this.addMoney = function (amount) {
        return this.money += amount + ' ' + UI_UNIT;
    };
}

var piggyBank = new MoneyMaker(1000);
piggyBank.addMoney(1);

Equality Operators

Use === and !== instead of == and != to avoid type coercion.

var same = (a === b);

Ternary Operator

Use ternary operator to conditionally assign a value, not as a shortcut for an if statement.

var val = (a > b) ? b : a;

Compound Statements

Opening braces should be at the end of the line that begins the compound statement. Braces should be used around all statements even one-liners. Variables should not be declared in the initialization section of a for statement.

// put opening brace on same line as statement
if (true) {
    // ...
}

while (condition) {
    // ..
}

do {
    // ...
} while (value);

switch (true) {
    case 1:
        /* fall through */
    case 2:
        fn();
        break;
    default:
        return undefined;
}

try {
    // ...
} catch (e) {
    // ...
} finally {
    // ...
}


// define loop variables at the beginning of scope
var i,
    len;

for (i = 0, len = 10; i < 10; i++) {
    // ...
}

White Space

Blank lines improve readability by setting off sections of code that are logically related.

It's a good idea to add blank lines:

(function() {
    'use strict';
    var a,
        b = 10;

    function theFunction() {

        if (true) {

            if (!!0) {

                while (--b) {
                    a = b;

                    // HACK: nonsense
                    b = a;
                }
            }
        }
    }

    function aFunction() {
        // ...
    }
})();

JsHint

We use jsHint to enforce a consistent coding style and to help prevent JavaScript errors. Using JsHint inside Visual Studio - the basics

make sure your code passes JsHint. If you must include code that doesn't pass add a directive to make it pass.

/*global jQuery*/ // jshint option: to declare jQuery a valid global
(function ($) {
    'use strict';
    var module = {
        prop: false
    };

    function toggleProp() {
        /*jshint validthis: true*/ // jshint option: ignore 'possible strict mode violation'
        this.prop = !this.prop;
    }

    toggleProp.apply(module);
})(jQuery);

Strict Mode

Always use strict mode.

(function () {
    'use strict'; // turn on strict mode

    // do something

})();

Unit Tests

Automated testing is not optional. You will have to change the way you write JavaScript to make it testable.

    TODO
    QUnit
        Some semantics:
        Unit Tests
            self contained
            targetted
            any markup to test against is embedded in test js file
            not dependent on CSS files
            Will be included in CI build
        Integration Tests
            may depend on production markup created by executing controls
    build appTarget qunit
        generates qunit.html file to run tests

JavaScript should be created using Test Driven Development. We use QUnit as our testing framework. We have had a few iterations of unit test patterns. You will see these in the codebase. Model your new tests off of the Health Concierge project.

Command-line QUnit test runner coming soon

You will soon be able to execute > build myapp qunit to create a qunit html file and execute your application's javascript tests from the command line and a CI build.

Test files should be located at \unittests\web\[ApplicationName]\. The paths to individual files within \unittests\web\[ApplicationName]\ should match the locations of the production files under \production\web\[ApplicaitonName]\.

Production
|-- Web
    |-- <Application Name>
        |-- modulefile.js

UnitTests
|-- Web
    |-- <Application Name>
        |-- config.json (optional)
        |-- modulefile.tests.js
        |-- <Application Name>.tests.html (built by cli)

JavaScript Architecture

jQuery usage

Do not default to using jQuery for everything. It is a large library that includes much more functionality than is typically needed.

jQuery promotes some no-so-great practices. These should be avoided:

And move styling and animation decisions to CSS, where they belong.

Updating styles via class name rather than setting styles from script is also faster

Delegated Event Handlers

Don't use 2 event handlers when 1 will do.

<menu id="addingMachine">
    <button type="button" data-value="1">add 1</button>
    <button type="button" data-value="2">add 2</button>
    <!-- 100 more buttons -->
</menu>

<script>
var total = 0;
dom('#addingMachine').on('click', '[data-value]', function (e) {
    total += parseInt(dom(e.target).attr('data-value'), 10) || 0;
});
</script>

Write Modular Code

Prefer small, decoupled, independent, and cohesive modules of functionality over big architectures.

Why modules?

Now that we're delivering page content faster, the next step is to ensure that our JavaScript is loaded and the application is interactive as soon as possible. To do that, we need to minimize the amount of JavaScript we use: smaller payload over the wire, fewer lines of code to parse, faster to execute. To make sure we only download the JavaScript necessary for the page to work, we needed to get a firm grip on our dependencies.

To do this, we opted to arrange all our code as CommonJS modules, delivered via AMD. This means that each piece of our code explicitly declares what it needs to execute which, firstly, is a win for developer productivity. When working on any one module, we can easily understand what dependencies it relies on, rather than the typical browser JavaScript situation in which code depends on an implicit load order and globally accessible properties.

Define all dependencies, do not access them from global scope within a module. This includes objects like window and document as well as other custom or third-party scripts. This improves performance as there is one less level to walk in the scope-chain in order to find an object definition. More importantly, it improves maintainability by documenting dependencies.

Private Module

Use this pattern when: * you don't need to export a JavaScript API to another JavaScript module/file * your unit tests aren't dependent on re-running the IFFE

If you don't need to export or import any functionality wrap it in an Immediately-Invoked Function Expression to prevent variable name collisions and keep the global namespace clean.

(function (document) {
    'use strict';
    
    var privateVar = true,
        danceparty = document.getElementById('danceparty');

    if (privateVar) {
        danceparty.className = 'dance-time';
    }
})(document);

Module with Public API

Use this pattern if you must create an API that other JavaScript code will call into.

We use the AMD pattern for modularization.

We use a custom-built implementation of the AMD loader that reinitializes all module constructors for each QUnit test - this enables us to isolate test runs and prevent state leakage between multiple QUnit tests.

// explicitly assign your module to window property
define("InterstitialModal", ["minQ", "InterstitialModal"], function($, dependency2) {
    'use strict';

    // setup
    var privateCount = 0

    return {
        count: function () {
            return privateCount;
        },

        increment: function () {
            return ++privateCount;
        },

        decrement: function () {
            return --privateCount;
        }
    }
});

NOTE: we used to use the revealing module pattern for this. It was not as testable as AMD modules due to the non-reinitialized state that can be captured in the IFFE and cause conflicts between unit test runs. The AMD pattern also simplifies namespace creation, provides better documentation, and reduces file-include-order dependencies.

Responsive Design

There is no mobile web, there is no http://m.webmdhealth.com site, all functionality should be available to all of our supported browsers/devices provided by the same HTML/CSS/JavaScript.

For browsers that support CSS media queries, our pages should render and look/work well at any resolution from 320px to 1024px+.

We do not reccommend site-wide breakpoints for "mobile" or "tablet" or "desktop" - let the content and layout on individual pages drive the layout decisions.

A Lot of Media Queries?

If you find yourself needing a lot of media queries in your layout's CSS, it might be a sign that your layout is too brittle.

Organzizing media queries

Most of our core applications have LESS files broken up into small modules and components, concatenated into one LESS file. The responsive portion of the application is usually treated as one of the components, separated into a responsive.less file. This makes the responsive file fairly obtuse, with components mixed together. Debugging requires a more ctrl+f and dev tool hunting than I'd like. Wouldn't it be nice if the component's responsive bits were with the rest of the styles?

This is how we'd typically organize a responsive.less. Pretend .foo and .bar are two unrelated pieces of your app.

@media screen and (max-width: 768px) {
    .foo {color: red;}
    .bar {color: blue;}
}

@media screen and (max-width: 550px) {
    .foo {color: green;}
    .bar {color: yellow;}
}

We can greatly simplify that file by calling some mixins.

@media screen and (max-width: 768px) {
    .screen-width-768px();
}

@media screen and (max-width: 768px) {
    .screen-width-550px();
}

...and now at the bottom of our .foo component file, we can define the mixins.

.screen-width-768px() {
    .foo {color: red;}
}

.screen-width-550px() {
    .foo {color: green;}
}

Ditto with our .bar file. This is better than defining a new media query in every component file. LESS is smart enough to combine mixins when you define them in more than one place, but it is not smart enough to combine media queries.

There are some caveats. If someone calls .screen-width-xxx() in their app and it is already in use someplace common, the nav for example, they would be duplicating styles by calling the mixin twice. We should be careful about this when using common breakpoints and namespacing application specific mixins.

Accessibility

Wiki Documentation

Keyboard - some people are unable to use, or prefer not to use the mouse or trackpad. Our site needs to be fully functional with just a screen and keyboard.

Screenreader

VPATs

VPATs for our products

This is the list of tests we need to verify to report that we are technically compliant with 508C. New applications MUST conform to these rules.

cannot be accomplished in any other way. The content of the text-only page shall be updated whenever the primary page changes.

functional text that can be read by Assistive Technology.

a plug-in or applet that complies with §1194.21(a) through (l). Just avoid applets and plugins (no more Flash)

functionality required for completion and submission of the form, including all directions and cues.

Performance

Optimize Images

Use tinypng or similar tool to compress png (and other image) files to speed up downloads.

Limit # of http requests

Maximize cacheability

Referenced CSS, JavaScript and image files will automatically contain caching headers.

Can your AJAX request be cached client-side (with HTTP caching)? If so, add the proper HTTP headers server-side.

Include Order

CSS in the head, scripts at the bottom of the body.

Do not depend on JavaScript for initial Render

The following is from Twitter(https://blog.twitter.com/2012/improving-performance-twittercom)

Looking at the components that make up this measurement, we discovered that the raw parsing and execution of JavaScript caused massive outliers in perceived rendering speed. In our fully client-side architecture, you don't see anything until our JavaScript is downloaded and executed. The problem is further exacerbated if you do not have a high-specification machine or if you’re running an older browser. The bottom line is that a client-side architecture leads to slower performance because most of the code is being executed on our users’ machines rather than our own.

There are a variety of options for improving the performance of our JavaScript, but we wanted to do even better. We took the execution of JavaScript completely out of our render path. By rendering our page content on the server and deferring all JavaScript execution until well after that content has been rendered, we’ve dropped the time to first Tweet to one-fifth of what it was.

Working with the Design Team

Designers should be considered an integral part of our development teams. They have great knowledge of both visual and user experience design. It's important to collaborate with the design team when iterating on product features.

Mockups are great but they shouldn't be used to replace face-to-face communication.

Do not expect (or wait for) pixel-perfect mockups. Question the fidelity of design mockups. Don't necessarily code exactly to mockups. Look for opportunities to leverage standard styles.

Do not expect the site to look the same on different browsers.

Tools

References

This guide borrows heavily from around the net and Nicholas Zakas' book Maintainable Javascript.

http://taitems.github.io/Front-End-Development-Guidelines/ http://isobar-idev.github.io/code-standards/ http://www.mediawiki.org/wiki/Front-End_Coding_Standards

<!-- ## TODO CSS Specificity Wars (http://www.stuffandnonsense.co.uk/archives/css_specificity_wars.html) Developer settings JsHint .LESS tools VS2012 text editor settings IE8 represents ~40% of our traffic today. General Pattern Library Consistency Hacks will break, prefer progressive enhancement Browsers are different, adopt a development stratagy to embrace this and not try to patch over it Use the right technology for the job Animations belong in CSS Transitions and animations HTML Avoid adding tags just to style custom attributes data- can be styled "[data-expanded=true]" works in all of our supported browsers all lowercase JavaScript Javascript should be a last resort. Style Guide For new pages, include script references at the bottom of the markup improves performance - *performance link* simplifies code - no longer need $(document).ready event in most cases Understand scoping and closures Understand browser dom api Use JS to manage state, not style jsHint Shared WHS settings Modular AMD Do not pollute the global namespace Global objects are bad, avoid them. Prefer scripts that do not expose any global api When that isn't possible, create as small of an API as possible Changing an existing JavaScript API is expensive *JS maintenance is hard, we don't have a compiler to enforce correctness like C# Prefer small decoupled modules to large monolithic applications Do not reach for a third-party library as a first solution Do not use jQuery for everything Our proliferation of dependencies on jQuery have prevented us from updating versions since Progressive Enhancement Nice-to-have features can be considered progressive enhancements and excluded from less capable browsers. -->