Introduction §

Welcome to the 18F front end guide! This is where we keep all of our guidelines for "front end" design and development, from more general core concepts to specific technical guidelines.

What is Front end? §

The front end guild did a series of exercises to determine the fundamental differences between the front end design and front end developer roles at 18F. Using some of our own research methods, the front end guild came up with the following recommendations on knowing the difference between the two disciplines:

Front end designers design, write, and implement the presentational code base for websites and applications. They should have a clear understanding of design fundamentals and systems, such as interface style guides, responsive design, grid systems, front end frameworks, and accessibility best practices. Front end designers should feel comfortable creating and implementing design systems using semantic HTML5, CSS/Sass and be able to assist in debugging this aspect of the code base.

Front end developers architect, write, and implement the functional code base for websites and applications. They should have a clear understanding of client-side render and response, such as HTTP methods, API consumption, the browser loading/rendering pipeline, and accessibility best practices. Front end developers should feel comfortable developing and implementing client-side interactions and frameworks using semantic HTML5 and JavaScript, and should be able to help with debugging, testing, and performance optimization of the code base.

CSS §

Purpose

The purpose of the CSS coding styleguide is to create consistent CSS or preprocessor CSS code across 18F. The styleguide should be treated as a guide — rules can be modified according to project needs.

Linting §

The styleguide provides a method of linting Sass code to ensure it conforms to the rules in the styleguide. This linting tool will go through all Sass code and issue warnings wherever the code differs from the styleguide. We've created a specific .scss-lint.yml file that's configured to work with the css coding styleguide. There are three ways to setup linting:

  • on GitHub with Hound
  • locally with ruby
  • locally with node

On GitHub with Hound

  1. Go to Hound.
  2. Sign in with GitHub.
  3. Activate the respository through Hound.
  4. Add the .scss-lint.yml file to the base of your repository.

Locally with ruby

  1. Add the .scss-lint.yml file to the base of your repository.
  2. Install the scss-lint gem with gem install scss_lint
  3. Run scss-lint: scss-lint app/assets/stylesheets/

Locally with node (experimental!)

  1. Run npm install --save-dev @18f/stylelint-rules to download the package and save it to your package.json
  2. The package provides both a gulp task and a cli interface. Detailed usage instructions can be found in the README

Shortcomings

scss-lint

The scss-lint tool currently lacks the functionality to check these rules in the CSS coding styleguide:

  • Does not limit line width to 80 characters
  • Does not check for numeric calculations in parentheses
  • Does not sort properties in quite the order we want (defaults to alphabetical)

stylelint

This tool is still being evaluated, so not every rule in our current styleguide is supported by stylelint. scss-lint is purpose built for SCSS and is therefore a bit more feature rich. The following rules are currently not supported:

  • PropertySpelling
  • UnecessaryParentReference

That being said, if you want to avoid a dependency on ruby in your project and still benefit from reliable SCSS linting, please test out this tool!

Preprocessors §

The most supported CSS preprocessor at 18F is Sass (SCSS). Using this pre-processor means you'll get supported resources such as frameworks, libraries, tutorials, and a comprehensive styleguide as support.

In addition, 18F uses a .scss-lint.yml file to keep our CSS code compliant with our own styleguide.

That being said, any preprocessor is allowed as long as it's a sound project and has community support.

The recommended way to compile your Sass code is through node-sass, rather than Ruby Sass. This allows eliminating the Ruby dependency for projects that don't already require it and is the fastest method of compiling Sass.

Frameworks §

18F currently recommends two CSS frameworks. Team members can choose the framework that best meets project and design and development needs:

  1. Bourbon
  2. PureCSS

Rationale

These frameworks were chosen because they're relatively unopinionated about design decisions while still providing the helpers that make frameworks essential to fast and accurate frontend work, for example, solutions for responsive design, grids, and common design patterns. In addition, both frameworks, through modular design and excellent documentation, make it easy for the designer or developer to only use the parts that they need, rather than including a hefty library. Of the two, PureCSS is extremely lightweight, while Bourbon is a Sass mixin library that has extensions for a robust semantic grid (Neat), base scaffold (Bitters) and patterns (Refills).

18F specifically does not recommend using Bootstrap for production work because:

  1. It is difficult to adapt its opinionated styles to bespoke design work, and
  2. Its CSS style places semantic layout instructions directly in HTML classes.

Formatting §

Spacing

  • Where possible, limit CSS files’ width to 80 characters. See notes to see how to configure your text editor to 80 characters.
    • There will be unavoidable exceptions to this rule, such as URLs, or gradient syntax. Don’t worry.
  • Use soft-tabs with a two space indent.
  • Put one space after : in property declarations.
  • Put spaces before { in rule declarations.
  • Put a blank line between each selector block.
  • To close a selector block, put an unindented closing curly brace on a separate line.
  • Each declaration should appear on its own line for more accurate error reporting.
  • Do not indent selectors.
// Bad
.rule{
    margin:3px;text-align:center;}

// Good
.rule {
  margin: 3px;
  text-align: center;
}

.another_rule {
  margin: 3px;
}

Multiple selectors should each be on a single line, with no space after each comma, unless the selector is less than five chars.

// Bad
selector1, selector2 {
}

// Good
selector1,
selector2,
selector3 {
}

// Good
h1, h2 {
}

Property-value pairs

  • Put each pair on its own line.
  • Indent each pair one level.
  • End in a semicolon.
selector {
  name: value;
  name: value;
}

Spaces should separate values and operators in Sass expressions.

// Bad
selector {
  font-size: ($font-size+2em);
  font-size: ($font-size +2em);
}

// Good
selector {
  font-size: ($font-size + 2em);
}

Do not use shorthand declarations unless you need to explicitly set all the available values.

// Bad
margin: inherit 3em;

// Good
margin-bottom: 3em;
margin-top: 3em;

margin: 3em 4em 2em 1em;

Single-quote URLs and string values.

  background-image: url('/images/kittens.jpg');
  font-family: 'Helvetica', sans-serif;
  font-family: 'Lucida Grande', 'Helvetica', sans-serif;

Wrap numeric calculations in parentheses.

// Bad
.component {
  width: 100% / 3;
}

// Good
.component {
  width: (100% / 3);
}

Avoid arbitrary numbers that are repeated, or linked, or dependent on other parts of the code, (aka “magic numbers”).

// Bad
.component {
  top: 0.327em;
}

// Better
/**
 * 1. Magic number. This value is the lowest I could find to align the top of
 * `.foo` with its parent. Ideally, we should fix it properly.
 */
.component {
  top: 0.327em;
}

// Good
$align_top: 100%;
.component {
  top: $align_top;
}

Order

  • Use the following ordering:

    1. variables
    2. @extend directives
    3. @include directives
    4. declaration list (property: name;)
    5. media queries
    6. pseudo-states (:checked, :target, etc.) and pseudo-elements (::after, ::selection, etc.)
    7. nested elements
    8. nested classes
  • Use alphabetical order or type order for declarations. Pick one to keep the whole project consistent.

  • Place a new line before nested selectors unless they are after the first selector.

  • Treat nested includes, such as Neat's media includes — @include media($small-screen) — as a standard media query, rather than a Sass @include. So they would be sorted directly after the declaration list.

  • Place mixin calls with @content after nested selectors.

  • You may deviate the sorting order to better suit your project's needs, as long as it's consistent throughout the project.

// Bad
.module {

  .module-element {
    color: #fff932;
  }
}

// Good
.module {
  .module-element {
    color: #fff932;
  }
}

// Good
.module {
  $amount = 3;
  @extend .component;
  @include sizing($amount);
  margin-top: $amount * 1em;
  text-align: center;

  @include media($small-screen) {
    margin-top: ($amount + 10em);
  }

  &::before {
    content: "hello";
  }

  .module__ele {
    color: #fff932;
  }
}

Notes

How to set text editors to 80 characters

  • Sublime: Add a rulers setting with 80 as the value.
    • "rulers": [80]
  • Atom: Set the preferredLineLength setting to 80.
    • preferredLineLength: 80
  • Vim: Set two options in your .vimrc to wrap lines at 80 characters.
    • set formatoptions+=w
    • set tw=80

Units §

Measurements

  • Use rem units for font sizes with a px fallback. This can be done with the following mixin:
  @mixin font-size($sizeValue: 1.6) {
    font-size: ($sizeValue * 10) + px;
    font-size: $sizeValue + rem;
  }
  • Set the HTML font size to 10px to ensure that 0.1rem equals 1px.
  html {
    font-size: 10px;
  }
  • Use em units for positioning.
  • Use percentages when layout components stay relational to each other (e.g. a main content area that takes up 75% of the screen and a sidebar that takes up 25%).
  // Good
  .panel-a {
    width: 25%;
  }

  .panel-b {
    width: 75%;
  }
  • Use px units for when a measurement shouldn't change based on user set font size or browser zooming or for when requiring pixel values below 5.
  // Bad
  selector {
    border-width: 55px;
  }

  // Good
  selector {
    border-width: 2px;
  }
  • Use unitless values for line-height as this will inherit values from the font-size.
  • Use up to 10 decimal places in em units to ensure accuracy.
  // Good
  .body_copy {
    @include rem-font-size(1.4);
    // Line height will now be 1.8 of 1.4rem, or 2.5rem.
    line-height: 1.8;
  }

  // Good
  .container {
    height: 12em;
    margin-left: 10.6666666667em;
    width: 82.5%;
  }
  • Do not use a unit with 0.
  // Bad
  width: 0px;

  // Good
  width: 0;
  • Always use a unit for dimensions, margins, borders, padding, and typography.
  // Bad
  border-width: 12;

  // Good
  border-width: 12px;

Colors

  • Use hex notation first, or then rgb(a), or hsl(a).
  • Both three-digit and six-digit hexadecimal notation are acceptable.
  • When denoting color using hexadecimal notation, use all lowercase letters.
  • When using HSL or RGB notation, always add a single space after a comma and no space between parentheses and content.
// Bad
color: #FFF;
color: rgb( 255, 0, 0 );

// Good
$light: #fff;
color: $light;

// Good
$primary: #fe9848;
color: $primary;

// Good
$secondary: rgba(255, 100, 255, 0.5);
color: $secondary;
  • If you use an rgba rule, include a fallback value in hexadecimal.
  // Good
  .illustration {
    background-color: #eee; // fallback
    background-color: rgba(221, 221, 221, 0.75);
  }

Naming §

  • HTML elements should be in lowercase.
  body,
  div {
  }
  • Classes should be lowercase.
  • Avoid camelcase.
  • Name things clearly.
  • Write classes semantically. Name its function not its appearance.
  // Bad
  // Avoid uppercase
  .ClassNAME { }

  // Avoid camel case
  .commentForm { }

  // What is a c1-xr? Use a more explicit name.
  .c1-xr { }
  • Avoid presentation- or location-specific words in names, as this will cause problems when you (invariably) need to change the color, width, or feature later.
  // Bad
  .blue
  .text-gray
  .100width-box

  // Good
  .warning
  .primary
  .lg-box
  • Be wary of naming components based on content, as this limits the use of the class.
  // Danger zone
  .product_list

  // Better
  .item_list
  • Don't abbreviate unless it’s a well-known abbreviation.
  // Bad
  .bm-rd

  // Good
  .block--lg
  • Use quotes in type pseudo selectors.
  // Good
  .top_image[type="text"] {
  }
  • Name CSS components and modules with singular nouns.
  .button {
  }
  • Name modifiers and state-based rules with adjectives.
  .is_hovered {
  }
  • If your CSS has to interface with other CSS libraries, consider namespacing every class.
  .f18-component

Naming Methodologies

When it comes to naming, the most important thing is consistency. The recommended way to do this is using an existing methodology like BEM, or use a custom one that’s clearly defined.

BEM

BEM (Block, Element, Modifier) structures CSS such that every entity is composed of (you guessed it) blocks, elements and modifiers. From Harry Roberts:

The point of BEM is to tell other developers more about what a piece of markup is doing from its name alone. By reading some HTML with some classes in, you can see how – if at all – the chunks are related; something might just be a component, something might be a child, or element, of that component, and something might be a variation or modifier of that component.

18F generally recommends using a modified BEM methodology outlined in the next subsection. However, you might want to use standard BEM when:

  • You need a naming scheme that general CSS developers will already be familiar with or an existing naming scheme hasn’t been consistent enough.
  • When you want to use JavaScript to modify the BEM class names dynamically.

Here is an example of BEM in SCSS:

// block
.inset {
  margin-left: 15%;

  // element
  .inset__content {
    padding: 3em;
  }
}

// modifier
.inset--sm {
  margin-left: 10%;

  .inset__content {
    padding: 1em;
  }
}

// modifier
.inset--lg {
  margin-left: 20%;
}

Suggested custom methodology

The 18F recommendation for a naming methodology is a modified version of BEM. It still uses blocks, sections within blocks and modifiers, but with an abbreviated syntax.

.accordion
.accordion-item
.accordion-item-selected

.nav_bar
.nav_bar-link
.nav_bar-link-clicked

Naming methodology resources

js- flagged classes

Don't attach styles to classes with a js- flag. These classes are reserved for javascript.

// Bad
.js-people {
  color: #ff0;
}

Rationale

A js- flagged class needs to be highly portable. Adding styles to it breaks that portability.

test- flagged classes

Don't attach styles to classes with a test- flag. These classes are reserved for testing hooks such as those used by selenium.

// Bad
.test-people {
  color: #ff0;
}

Inheritance §

Mixins

  • Use mixins for groups of properties that appear together intentionally and are used multiple times.
  @mixin clearfix {
    &:after {
      content: '';
      display: table;
      clear: both;
    }
  }
  • Use mixins for components to change size.
  • Use mixins when something requires parameters.
  @mixin size($width, $height: $width) {
    width: $width;
    height: $height;
  }
  // Bad
  @mixin transform($value) {
    -webkit-transform: $value;
    -moz-transform: $value;
    transform: $value;
  }

Extend

Be very careful with using @extend. It's a powerful tool that can have disastrous side-effects. Before using please consider:

  • Where is my current selector going to be appended?
  • Am I likely to be causing undesired side-effects?
  • How large is the CSS generated by this single extend?

If you're unsure of using @extend, follow these rules to avoid running into trouble:

  • Use @extend from within a module, not across different modules.
  • Use @extend on placeholders exclusively, not on actual selectors.
  • Make sure the placeholder you extend is present as little as possible in the stylesheet.

You can use mixins in place of selectors. While mixins will copy more code, the difference will often be negligible once the output file has been gzipped.

Architecture §

A site's architecture should be based on its goals and purposes. This means the guidance here should be adapted to different sites and situations.

Modular or component architecture

When using a modular or component architecture, every page is broken into a series of modular components. There are two sets of these components: components and modules. The architecture starts out with basic HTML element rules: HTML, p, a, form, etc tags that than have components and modules written on top of them. Components are very basic structure such as buttons, blurbs, navs, and positioning structures like insets, island, and enclosure. From here, modules are built with these components. This architecture also attempts to keep the specificity trend in an upwards curve as you move down in the file (more on this to come).

  • Start with an elements file for all tag rules (a, h1-h5, p, *, html, body).
  • Create component files for each structural element, such as buttons, navs, etc. These are mainly class-based and use BEM or another naming scheme.
  • Create more specific structure with modules. For instance, if the logo image and text needs very specific treatment, use a module.
    • Build modules from components through mixins, extends, and HTML.
    • Modules can have higher specificity, it’s fine to use deeper nesting.
  • Have an overrides file or folder comprised of global rules that are meant to override components and modules.
    • These can be generic utilities.
    • A good thing to put here are breakpoint-specific rules, such as hiding something at small breakpoints.

File structure

_elements.scss
_mixins.scss
_typography.scss
_util.scss
_vars.scss
component/_blurb.scss
component/_button.scss
component/_island.scss
component/_sub_nav.scss
module/_logo.scss
module/_progress_bar.scss
lib/bourbon.scss
lib/neat.scss
_overrides.scss

For the util, typography, elements, and overrides files, once they grow too large (300 lines or more) in size, split them into their own folder with sub files.

elements/_all.scss
elements/_p.scss
elements/_h.scss
typography/_body.scss
typography/_links.scss
overrides/_breakpoints.scss
overrides/_util.scss
util/_center.scss
util/_clearfix.scss

Importing

As you likely know, CSS rules that are later in the file override earlier rules. This means Sass imports can be used to control inheritance and specificity.

  • Start with base elements.
  • Move to single nested classes and utils.
  • Move next to more specific classes, often with nesting.
  • Move next to overrides, possibly with !important rules.
  • Import alphabetically.
  • Only modify import order for groups of files, not specific files.
// Bad
@import 'module/logo';
@import 'component/mask';
@import 'component/button'; /* Has to be imported after "mask" */

// Good
@import 'component/button';
@import 'component/mask';
@import 'module/logo';

Specificity §

  • IDs should be reserved for JavaScript. Don’t use IDs for styles.
  // Bad
  #component { }

  // Good
  .component { }
  • Don't nest more than 3 layers deep.
  • Do not fix problems with !important. Use !important purposefully.
  // Bad
  .component {
    width: 37.4% !important;
  }

  // Good
  .hidden {
    display: none !important
  }
  • Keep specificity low and trend upwards in specificity as you move further down file. See the specificity graph section for more info.
  • Don't use unnecessary tag selectors.
  // Bad
  p.body_text { }

  // Good
  .body_text
  • If you have to hack specificity, use a safe hack: the multi class.
  // multi-class hack
  .component.component { }

Specificity graph

An easy rule to use when dealing with specificity is to start from a low specificity and curve to higher specificity as you move towards the bottom of the output file. Since CSS rules get replaced by rules further down in the file, you'll override rules in an expected way.

There’s a tool that can graph your files’ specificity, CSS specificity graph. Run your final output file through this tool and strive for a curve trending upwards.

Resources

Rationale

With specificity comes great responsibility. Broad selectors allow us to be efficient, yet can have adverse consequences if not tested. Location-specific selectors can save us time, but will quickly lead to a cluttered stylesheet. Exercise your best judgement to create selectors that find the right balance between contributing to the overall style and layout of the DOM.

  • When modifying an existing element for a specific use, try to use specific class names. Instead of .listings-layout.bigger use rules like .listings-layout.listings-bigger. Think about ack/grepping your code in the future.

  • Use lowercase and separate words with hyphens when naming selectors. Avoid camelcase and underscores. Use human-readable selectors that describe what element(s) they style.

  • Attribute selectors should use double quotes around values. Refrain from using over-qualified selectors; div.container can simply be stated as .container.

  • IDs should be reserved for JavaScript. Unless you have a very good reason, all CSS should be attached to classes rather than IDs. When in doubt, use a class name. This prevents target confusion and allows CSS devs and JS devs to co-exist in the same code in peace. If you must use an id selector (#id) make sure that you have no more than one in your rule declaration.

Variables §

  • Create new variables in the following circumstances:
    • The value is repeated twice
    • The value is likely to be updated at least once
    • All occurrences of the value are tied to the variable (for example not by coincidence)
  • When building scss that will be used across multiple projects use the !default flag to allow overriding.
  $baseline: 1em !default;
  • The !global flag should only be used when overriding a global variable from a local scope.
  • Variables across the whole scss codebase should be placed in their own file.
  • When declaring color variables, don't base the name on the color content.
  // Bad
  $light_blue: #18f;
  $dark_green: #383;

  // Good
  $primary: #18f;
  $secondary: #383;
  $neutral: #ccc;
  • Be careful when naming variables based on their context.
  // Bad
  $background_color: #fff;
  • Don't use the value of dimensional variables in the variable name.
  // Bad
  $width_100: 100em;

  // Good
  $width_lg: 100em;
  • Name all used z-indexes with a variable.
  • Have a z-index variable for each z-index used, and a separate variable, possibly aliased for where the z-index is used.
  $z_index-neg_1: -100;
  $z_index-neg_2: -200;
  $z_index-1: 100;

  $z_index-hide: $z_index-neg_2;
  $z_index-bg: $z_index-neg_1;
  $z_index-show: $z_index-1;

Responsive Design & Breakpoints

  • Set variables for breakpoints at the top of your stylesheet. This functionality is built into Bourbon.
  $sm: new-breakpoint(min-width 0 max-width 40em $sm_cols);
  • Use variables to set the queries throughout so they are easy to adapt if necessary.
  • Place media queries nearest to the class they are affecting.
  • Rather than focusing on devices when deciding where to put breakpoints, focus on content; name breakpoint variables relative to each other.
  // Bad
  $iphone: new-breakpoint(min-width 0 max-width 640px 6);

  // Good
  $small: new-breakpoint(min-width 0 max-width 40em 6);
  $medium: new-breakpoint(min-width 0 max-width 60em 6);

Documentation §

Sass Comments

Be intentional when you use // (silent comments) versus /* */ (which are preserved in the CSS output). When in doubt, use //.

KSS

Use KSS for documentation. More information on KSS can be found on the official site.

Example

// Button
//
// Various buttons on the site.
//
// Markup
// <a class="button ">
//  <span class="button__text">Link</span
// </a>
//
// .button-modified - A button with a different style.
//
//
// Styleguide component.button
.button {
}

.button-modified {
}

Rationale

KSS is the most common CSS documentation method to date. While it’s not perfect, the generated documentation can be modified through templates.

JavaScript §

Dependencies §

The word "dependency" refers to all of the frameworks, libraries, and other tools that your project relies on. Dependency management is the process by which tools are incorporated into your project, removed and updated (for instance, when you need a new version of jQuery). Here are the tools that we recommend for managing dependencies:

Bower

Do not use Bower.

It's not needed and should be phased out and replaced by npm. More information can be found here: Why We Should Stop Using Bower – And How to Do It.

npm

npm informally stands for Node Package Manager, and is the package manager node uses. Its usage is very similar to Bower because the latter was inspired by the former.

npm instructions

  1. Get Node.js.
  2. To initialize your project, run npm init in your project directory, which will create a package.json.
  3. Install some dependencies with npm install --save [name], e.g.
    • jQuery: npm install --save jquery
    • D3: npm install --save d3@v3.5.5 (version 3.5.5)

npm installs its dependencies in the node_modules directory. Common conventions dictate that node_modules should be excluded from source control by adding it to your project's .gitignore, primarily because Node.js-friendly environments (such as 18F's deployment service, Cloud Foundry, and other such as Heroku) recognize the existence of package.json and automatically install dependencies as needed.

Install npm

We recommend that developers (note 1) install both node and npm through a tool called nvm. nvm (which stands for Node version manager) is a software that allows you to run multiple versions of node in different projects on the same computer. Its benefits include

  • Installs npm in a manner that doesn't require running sudo to install global packages.
  • Easily be able to switch between multiple node versions with a project configuration file or command.

To install on MacOSX or linux, follow the instructions on the nvm site. If you system has a c++ compiler setup, you'll likely be able to install it with this simple script:

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.6/install.sh | bash

Safely installing packages from npm

While npm is generally a safe environment to install code from, there are certain aspects of the system that are vulnerable to dangerous script execution. Luckily there are steps that can be taken to minimize these risks.

It's recommended that developers at 18F follow these guidelines when installing unknown or new packages.

npm allows various hooks to be executed during the install process. These scripts are where potential dangerous scripts can be executed. To limit this it's recommended to:

  1. install npm in a manner so sudo is never required. The 18F recommended way of doing this is to install with nvm.
  2. check which scripts will be run on install by running npm show $module scripts.
    • Each script under preinstall, install, postinstall will be run when installing.
    • Each script under postuninstall, preuninstall, uninstall will be run on uninstall.
  3. Pull a tarball of the whole package down to check that any scripts run during those steps are safe, wget http://registry.npmjs.org/$module/-/$module-version.tgz.
    • Check any files that are being run as part of the install scripts.
    • Check that the file in the package are generally what they are supposed to be.
  4. If unsure, install the packages without running any scripts with npm install $module --ignore-scripts.

Publishing

Scoping a package to the 18F npm org

18F has an npm organization called 18f that is meant to organize permissions and packages related to 18F. As an 18F developer, when publishing a package, you have the choice whether to scope a package to the 18F org or not. Scoped packages will always be prefixed with @18f/ before their package name and can have their permissions managed by people in the org. More information about scoped packages can be found on the npm documentation.

Guidance on when to scope a package or not
  • A package should not be scoped to 18F if it is not necessary for consumers (either gov or non-gov) of the package to be aware of 18F in order to use it.
    • Example: The Draft US Web Design Standards are used by many entities outside of 18f and government. A user does not need to know anything about 18F to use the Web Design Standards package.
  • A package should be scoped to 18F if its use cases fall mainly inside of 18F.
    • Example: @18f/stylelint-rules is scoped to 18f because it's an 18F specific linting configuration that's directly linked to the 18F guides site.
  • A package should be scoped to 18f to avoid naming conflicts.
    • Example: If 18F made a generic front end accordion to use across 18F sites, it should probably be scoped to @18f/accordion to avoid conflicts with all other accordions out there.
How to scope a package to 18F
  • Ensure you are part of the 18f npm org and have at least developer rights. This can be found on the 18f org team page.
    • If you don't have the proper access, ask in #g-frontend or #javascript slack channels and an admin will add you.
  • If the package has not been published to 18f yet, follow the instructions on npm for scoped packages.
  • If the package has already been published, it currently cannot be scoped, so may need to be renamed. See the npm documentation on existing packages for more information.
General tips for publishing
  • Use semver.
  • Include instructions on how to use the modules in the README.md. Start from npm install, as this is generally a convention on npm.
  • Test to ensure that your package works with any required versions of node and works on Windows.

Node and the browser

Using Node.js modules in the browser can be either straightforward or convoluted, depending on the project. Some project packages come with browser-ready .js files, whereas others require build tools such as Browserify or Webpack to translate some Node-specific JavaScript so that it can be run in browsers. Visit the #javascript channel on Slack if you need help with these tools.

Manual dependency management

Many dependencies consist of a single file and can be more easily incorporated simply by copying them into your project. We have some recommendations for how this should be done:

  1. Establish a specific directory for 3rd-party assets, e.g. js/vendor for JavaScript or assets/vendor for frameworks that consist of CSS, images and/or JavaScript.
  2. Download the assets to this directory, e.g. in your terminal:
  cd js/vendor
  curl -O http://code.jquery.com/jquery.min.js
  1. Add these dependencies to version control.

Libraries §

Scrolling

skrollr

Skrollr is stand-alone parallax scrolling library for mobile (Android + iOS) and desktop.

When to use

  • For building scrolling effects that have different requirements that very standard parallax. Skrollr has a lot of flexibility.
  • When supporting mobile web (even though it won't be a perfect experience) is a requirement.
  • Does not require jQuery

When not to use

  • If there are concerns the library has been inactive for too long and will break with newer browsers. skrollr hasn't been under active development since about September 2014.
  • If skrollr's method of controlling animation in the markup doesn't work for the project's architecture.

stellar.js

Parallax scrolling made easy, stellar.js.

When to use

  • For building simple parallax effects as well as more complex ones through the use of advanced configuration.

When not to use

  • If there are concerns the library has been inactive for too long and will break with newer browsers.
  • When some amount of experience on mobile is required. Stellar on mobile requires more configuration than something like skrollr.
  • When you don't want to require jQuery, instead see skrollr.

Tables

DataTables

DataTables is a jQuery tool to progressively enhance HTML tables with advanced interactive features, such as sorting, paging, filtering, etc.

When to use

  • Creating an interactive table that doesn't need to be configured or restyled very much from what DataTables offers.

When not to use

  • When the interactive table being created will be very custom and will not look and/or function like the basic examples on the data tables website.
  • When your site doens't require jQuery.
  • When elements around or interacting with the table use percentages for positioning. DataTables forces the table to use absolute units (pixels) which get manipulated. This means it can be problematic for responsive sites.

Pros

  • Is relatively easy to set up for a table in default styling and functionality.

Cons

  • Has a confusing syntax for expressing HTML in its API.
  • No ability to work without JavaScript.
  • Uses absolute units for everything rather than percentages, making it very hard to make it work with other responsive elements.
  • Tries to accomplish everything possible, which makes the API confusing.
  • Documentation on the API can be incomplete and hard to follow.

Frameworks §

Angular

AngularJS (commonly referred to as "Angular") is an open-source web application framework maintained by Google and by a community of individual developers and corporations to address many of the challenges encountered in developing single-page applications (Wikipedia).

When to use:

  • Sites with heavy front end, JavaScript UI interactions (single page apps) such as:
    • creating, updating, deleting of information without a server reload
    • real-time messaging platforms, such as chat or complex messaging such as email
    • complex data visualization dashboards
    • lazy-loaded from the back end
  • When the site's design specifies a single page app architecture over classic server request and response.
  • When the whole site will be built with Angular to maintain front end code consistency.

When not to use:

  • For a single or a few simple components (with the rest of the site not using Angular), instead see React or Web Components.
  • Exporting a module that isn't an Angular module.
  • If there is a strict requirement that the site should work for users that have JavaScript disabled.
  • If there already is an active M**V framework (Backbone, ampersand, Ember) being used on the site.
  • When the site's design doesn't benefit from a single page app architecture.
  • When the long-term maintenance dev team is very unfamiliar with Angular and don't have the resources to learn or hire for it.

Pros:

  • Takes care of a lot of boilerplate code for front end interactions.
  • Attempts to extend HTML itself, and was designed so less experienced devs could use it.
  • Being maintained and developed by Google generally means good support.

Cons:

  • While open source, is maintained primarily by Google.
  • Has been known to implement breaking changes in major version updates.
  • Built with Typescript and Dart, both of which are not ECMA standardized (as opposed to vanilla JS or ES6).
  • Has a steep learning curve and is very opinionated, meaning you learn Angular rather than JavaScript.

Backbone

Backbone.js is a JavaScript library with a RESTful JSON interface and is based on the model–view–presenter (MVP) application design paradigm (Wikipedia).

When to use:

  • A page design that requires dynamic data manipulation on the front end without a server request response, such as a todo app.
  • When a small front end framework is required due to performance constraints.
  • When the long-term dev maintenance team is unfamiliar with any full frameworks, such as Angular.
  • To use as a wrapper and rest data manipulation library around a view-only framework, such as React.
  • When the dev team is familiar enough with Backbone to know how to write maintainable Backbone code.

When not to use:

  • When the JavaScript components don't keep data or manipulate data, in which case Backbone's functionality is too heavy and not specific enough for just view rendering.
  • If there is a strict requirement that the site should work for users that have JavaScript disabled.
  • When another full JavaScript framework is already in use, such as Angular.
  • When working with a data source that is NOT RESTful. Backbone was built for RESTful services, instead see Flux.

Pros:

  • Relatively un-opinionated, meaning a lot of freedom in development.
  • Open source, and has an active, large community.

Cons:

  • Still requires a lot of boilerplate code (this can be mitigated by pairing with a library like Marionette)
  • Since it has very little structure, unexperienced programs can easily create unmaintainable code with Backbone.
  • Designed primarily for REST data.

React

React (sometimes styled React.js or ReactJS) is an open-source JavaScript library for creating user interfaces that aims to address challenges encountered in developing single-page applications (Wikipedia).

When to use:

  • Single page apps that requires data manipulation on the front end without a server side request/response architecture.
  • When there's a strong need to render JavaScript based UI on the server due to performance or accessibility reasons.
  • JavaScript UI that incorperates many nested components.
  • A UI with many components and updates that needs to be performance conscious.
  • When only a "view" framework is desired/required.
  • To ensure all front end components conform to a single standard.

When not to use:

  • When a complex build process is not feasible. React requires transforming "jsx" files to regular JavaScript.
  • When developers unfamiliar with JSX and don't have time to learn.
  • While open source, is maintained primarily by Facebook.

Flux

Flux is not a framework, nor is it M**VC. It's a software architecture for writing complex single page applications.

When to use:

  • A complex JavaScript app that requires both viewing and modifying (CRUD) data in a UI rendered on the client. Flux will likely be overkill for apps that don't modify data in any way.
  • When the data service for the front end is REST and/or something besides REST, such as Websockets.
  • An app thats data flow has grown or will grow overly complex.

When not to use:

  • Applications that don't require any updating (create, update, delete) of data.
  • When the cost of updating an app's architecture to flux is more than the cost of writing the software as it exists.

Pros:

  • Easily add non-REST services to a front end, in a transparent way.
  • Cleans up complex data flow by using uni-directional data flow.
  • Cleans up complex async behavior and nested callbacks by using an evented system and functionality to wait for data.
  • Can use simple JavaScript objects rather than a complex framework.
  • Easily tie components together in a clean way.
  • Requires little 3rd party software.

Cons:

  • More verbose in file and directory structure.
  • Finding best way to use can be difficult for beginners.
  • Can be hard to find a good structure when beginning.

Style / Linting §

We recommend adhering to the Airbnb JavaScript style guide.

Maintaining stylistic consistency across 18F's code helps lower the barrier to jumping in and helping with or reviewing other projects because we'll all be familiar with reading and working with code that looks similar. Having consistent rules for styling also removes generally non-productive discussions (aka bikeshedding) around personal code-formatting preferences.

eslint is our preferred tool for analyzing and flagging (aka "linting") JavaScript that is out of line with a set of stylistic rules. There are plugins to integrate eslint with nearly every code editor and build system, as described at http://eslint.org/docs/user-guide/integrations. Using an eslint plugin with your editor makes it easier to quickly see non-conforming lines of code on the spot, as shown below:

Atom eslint plugin screenshot

Airbnb provides npm packages of eslint rules that implement their style guide.

Each link above has instructions for installing the required npm packages and configuring eslint to use the installed rules. These should be installed for each project, and saved in each project's package.json.

Generally the process is to npm install the required modules and peer dependencies, for example:

# These version numbers will change so please follow the instructions at the
# linked packages for up-to-date instructions.
npm install --save-dev eslint@3.15.0 eslint-config-airbnb eslint-plugin-jsx-a11y@3.0.2 \
  eslint-plugin-import@2.2.0 eslint-plugin-react@6.9.0

and then create a local file configuration within your project called .eslintrc that looks like:

{
  "extends": "airbnb"
}

For more information on configuring eslint, see its documentation at http://eslint.org/docs/user-guide/configuring.

Web Components §

Initial Impressions

First, check out this article and its follow-up for some background on whether Web Components (or, more specifically, custom elements) are ready for production. TL;DR:

  • Custom element polyfills (needed for every browser except Chrome) are indeed ready for production.
  • If you need the template element and the so-called shadow DOM for style encapsulation, you're out of luck: outside of Chrome, only Firefox supports it, and only with a hidden setting. Things might change soon though, as Edge (ex-IE) has also recently implemented <template> element. (The polyfills for shadow DOM, particularly, are uuuuugly.)
  • The JavaScript libraries that expose similar APIs for defining web components are a mixed bag, as you'll see below.

My first stop when sussing out web components was WebComponents.org, which appears to be a collaboration between Google (makers of Polymer), Mozilla (makers of x-tag) and other open web technology folks. There's also CustomElements.io, which serves as a showcase for custom elements made primarily with Polymer and x-tag. I also enjoyed poking around the examples on component.kitchen, which are all available on GitHub.

So, what about the tools?

x-tag

I tried Mozilla's x-tag first, and was impressed. The API is simple, I liked that you can declare components in vanilla JS, and I was able to get something working in Chrome quickly. However, I was forced to abandon it after running into untraceable errors in x-tag core when testing in IE9. x-tag also relies on the same polyfills as Polymer, which are a pain to get and update (see below for more details). Survey says:

:-1: I couldn't get it working in IE9—or any browser other than Chrome, for that matter.

Polymer

Google's Polymer Project fully embraces HTML imports, which are a way to encapsulate your component's contents, behavior and appearance in a separate HTML file. It's a neat framework, but it also assumes that you want all of the bells and whistles, including two-way data binding and the shadow DOM. In my testing it also appeared to be wholly incompatible with IE9, and thus basically unusable at 18F.

:warning: The suite of polyfills that Polymer requires also entails an unnecessarily convoluted build process if you don't need all of its features. Documentation for the polyfills is sparse, and I was unable to find any mention of the errors that I encountered in IE9. For future reference, it's best to get everything from a CDN or install them from npm or bower. Also, if you don't need the shadow DOM, save yourself some bytes and use the bundled webcomponents-lite.js, which just provides custom elements and HTML imports.

:-1: Polymer gets my thumbs down for trying to do too much.

Vanilla JS (document.registerElement())

Next up, I tried out Andrea Giammarchi's polyfill for the core custom elements API, the document.registerElement() function. Andrea's polyfill has some major advantages over x-tag and Polymer:

  1. At just 3K minified and zipped, it's tiny compared to Polymer's hefty 150K payload (after you include all of the necessary polyfills). Because x-tag relies on many of the same polyfills (which involve a
  2. It works in IE9 if you include either aight or Andrea's dom4 polyfill.
  3. The API does exactly what the spec says it should, and nothing more. No two-way data binding, no event delegation, no performance-hobbling shadow DOM shims (or shams).

:+1: Use this! It works great!

Bosonic

A link to Bosonic features prominently on WebComponents.org, and claims support for IE9. Unlike x-tag and Polymer, Bosonic simply follows the web component specs and uses a transpiler to convert HTML imports into vanilla JS and CSS at runtime. Check out the docs for more info. The platform and runtime JS files weigh in at about 82K total.

:question: I didn't get a chance to try this one, but it's worth a look if you need HTML imports and shadow DOM.