Code Guide

Guidelines for flexible and maintainable HTML and CSS

Every line of code should appear to be written by a single person, no matter the number of contributors.*


TL;DR


HTML

Naming Conventions *

Semantics

Use meaningful names; use structural or purposeful names over presentational.

Use clear, thoughtful, and appropriate names for HTML classes. The names should be informative both within HTML and CSS files.

Practicality over purity

Strive to maintain HTML standards and semantics, but not at the expense of practicality. Use the least amount of markup with the fewest intricacies whenever possible.

Avoid systematic use of abbreviated class names. Don't make things difficult to understand.

Example with bad names:

<div class="cb s-scr"></div>

.s-scr {
  overflow: auto;
}

.cb {
  background: #000;
}

Example with better names:

<div class="column-body is-scrollable"></div>

.is-scrollable {
    overflow: auto;
}

.column-body {
    background: #000;
}

CSS

Before you continue reading, I highly recommend you study this reading list - it is the go-to resource for resources related to writing scalable, object-oriented CSS.

Our Codebase

We use a subset of SCSS for style generation. These naming conventions are adapted from the work being done in the SUIT CSS framework (and @fat at Medium). Which is to say...

It relies on structured class names and meaningful hyphens (i.e., not using hyphens merely to separate words). This is to help communicate the relationships between classes.

Principles

Object-orientation

Object-orientation is a programming paradigm that breaks larger programs up into smaller, in(ter)dependent objects that all have their own roles and responsibilities.

When applied to CSS, we call it object-oriented CSS, or OOCSS (coined by Nicole Sullivan). OOCSS deals with the separation of UIs into structure and skin: breaking UI components into their underlying structural forms, and layering their cosmetic forms on separately. This means that we can recycle common and recurring design patterns very cheaply without having to necessarily recycle their specific implementation details at the same time. OOCSS promotes reuse of code, which makes us quicker, as well as keeping the size of our codebase down.

The Single Responsibility Principle

The single responsibility principle is a paradigm that, very loosely, states that all pieces of code (in our case, classes) should focus on doing one thing and one thing only. More formally:

…the single responsibility principle states that every context (class, function, variable, etc.) should have a single responsibility, and that responsibility should be entirely encapsulated by the context.

What this means for us is that our CSS should be composed of a series of much smaller classes that focus on providing very specific and limited functionality. This means that we need to decompose UIs into their smallest component pieces that each serve a single responsibility; they all do just one job, but can be very easily combined and composed to make much more versatile and complex constructs.

Javascript

syntax: js-<targetName>

JavaScript-specific classes reduce the risk that changing the structure or theme of components will inadvertently affect any required JavaScript behaviour and complex functionality. It is not neccesarry to use them in every case, just think of them as a tool in your utility belt. If you are creating a class, which you dont intend to use for styling, but instead only as a selector in JavaScript, you should probably be adding the js- prefix. In practice this looks like this:

<a href="/login" class="btn btn-primary js-login"></a>

Again, JavaScript-specific classes should not, under any circumstances, be styled.

Utilities

Utility classes are low-level structural and positional traits. Utilities can be applied directly to any element; multiple utilities can be used together; and utilities can be used alongside component classes.

Utilities exist because certain CSS properties and patterns are used frequently. For example: floats, containing floats, vertical alignment, text truncation. Relying on utilities can help to reduce repetition and provide consistent implementations. They also act as a philosophical alternative to functional (i.e. non-polyfill) mixins.

<div class="u-clearfix">
  <p class="u-textTruncate">{$text}</p>
  <img class="u-pullLeft" src="{$src}" alt="">
  <img class="u-pullLeft" src="{$src}" alt="">
  <img class="u-pullLeft" src="{$src}" alt="">
</div>

u-utilityName

Syntax: u-<utilityName>

Utilities must use a camel case name, prefixed with a u namespace. What follows is an example of how various utilities can be used to create a simple structure within a component.

<div class="u-clearfix">
  <a class="u-pullLeft" href="{$url}">
    <img class="u-block" src="{$src}" alt="">
  </a>
  <p class="u-sizeFill u-textBreak">
    …
  </p>
</div>

Components

Syntax: <componentName>[--modifierName|-descendantName]

Component driven development offers several benefits when reading and writing HTML and CSS:

You can think of components as custom elements that enclose specific semantics, styling, and behavior.

ComponentName

The component's name must be written in camel case.

.myComponent { /* … */ }

<article class="myComponent">
  …
</article>

componentName--modifierName

A component modifier is a class that modifies the presentation of the base component in some form. Modifier names must be written in camel case and be separated from the component name by two hyphens. The class should be included in the HTML in addition to the base component class.

/* Core button */
.btn { /* … */ }

/* Default button style */
.btn--default { /* … */ }

<button class="btn btn--primary">…</button>

componentName-descendantName

A component descendant is a class that is attached to a descendant node of a component. It's responsible for applying presentation directly to the descendant on behalf of a particular component. Descendant names must be written in camel case.

<article class="tweet">
  <header class="tweet-header">
    <img class="tweet-avatar" src="{$src}" alt="{$alt}">
    …
  </header>
  <div class="tweet-body">
    …
  </div>
</article>

componentName.is-stateOfComponent

Use is-stateName for state-based modifications of components. The state name must be Camel case. Never style these classes directly; they should always be used as an adjoining class.

JS can add/remove these classes. This means that the same state names can be used in multiple contexts, but every component must define its own styles for the state (as they are scoped to the component).

.tweet { /* … */ }
.tweet.is-expanded { /* … */ }

<article class="tweet is-expanded">
  …
</article>

Variables

Syntax: <property>-<value>

Variable names in our CSS are structured to provide strong associations between property and use.

The following variable definition is a color property, with the value greenCaribbean.

$color-greenCaribbean: #00BC9B;

Colors

When implementing feature styles, you should only be using color variables provided by colors.scss. When adding a color variable to colors.scss, use hex instead of RGB for colors.

Right:

color: #000;

Wrong:

color: rgba(255,255,255,.3);

z-index scale

Please use the z-index scale defined in z-index.scss.

@zIndex-1 - @zIndex-9 are provided. Nothing should be higher then @zIndex-9.

Font Styles

With the additional support of web fonts font-weight plays a more important role than it once did. Different font weights will render typefaces specifically created for that weight, unlike the old days where bold could be just an algorithm to fatten a typeface. Obvious uses the numerical value of font-weight to enable the best representation of a typeface. The following table is a guide:

Raw font weights should be avoided. Instead, use the appropriate font mixin: .font-sansI7, .font-sansN7, etc.

The suffix defines the weight and style:

N = normal
I = italic
4 = normal font-weight
7 = bold font-weight

Refer to type.scss variables.scss for type size, letter-spacing, and line height. Raw sizes, spaces, and line heights should be avoided outside of type.scss variables.scss.

@fontSize-micro
@fontSize-smallest
@fontSize-smaller
@fontSize-small
@fontSize-base
@fontSize-large
@fontSize-larger
@fontSize-largest
@fontSize-jumbo

Line Height

type.scss variables.scss also provides a line height scale. This should be used for blocks of text.

@lineHeight-tightest
@lineHeight-tighter
@lineHeight-tight
@lineHeight-baseSans
@lineHeight-base
@lineHeight-loose
@lineHeight-looser

Letter spacing

Letter spacing should also be controlled with the following var scale.

@letterSpacing-tightest
@letterSpacing-tighter
@letterSpacing-tight
@letterSpacing-normal
@letterSpacing-loose
@letterSpacing-looser

Mixins

mixin syntax: m-<propertyName>

// this gives items a max width and centers them using margins
@mixin m-centerAlign($max-width) {
  display: block;
  max-width: $max-width;
  margin-left: auto;
  margin-right: auto;
}

Single declarations

In instances where a rule set includes only one declaration, consider removing line breaks for readability and faster editing

/* Single declarations on one line */
.span1 { width: 60px; }
.span2 { width: 140px; }
.span3 { width: 220px; }

/* Multiple declarations, one per line */
.sprite {
  display: inline-block;
  width: 16px;
  height: 15px;
  background-image: url(../img/sprite.png);
}

Shorthand notation

Strive to limit use of shorthand declarations to instances where you must explicitly set all the available values.

Common overused shorthand properties include:

padding
margin
font
background
border
border-radius

Often times we don't need to set all the values a shorthand property represents.

/* Bad example */
.element {
  margin: 0 0 10px;
  background: red;
  background: url("image.jpg");
  border-radius: 3px 3px 0 0;
}

/* Good example */
.element {
  margin-bottom: 10px;
  background-color: red;
  background-image: url("image.jpg");
  border-top-left-radius: 3px;

  border-top-right-radius: 3px;
}

Architecture

Stylesheet Directory Structure

├── base  // normalize.css, styling elements, global styles, typography, forms, lists and tables
│   └── IE
├── components  // reusable, modular design elements
├── layout  // grid, layouts
├── plugins  // for vendor utilities used
├── state  // active/inactive, hidden/visible; JS
└── theme  // added layer of design (colors, fonts); multiple themes, variations on style of components

Stuff to Avoid

Avoid ID’s

ID’s are really hard. They are to specific and mostly necessary for JS-Hooks. The better way is to use classes.

Avoid !important

If you need this, than goes something wrong at your CSS-architecture, because you need this only if you want to overwrite styles. If your structure is clean, than you will don’t need this. It’s like to shot with a cannon to sparrows.

Avoid child-selector

Why I avoid child-selector? It is yet a good CSS-Feature? No I don’t think so. At my start I used it often, but more and more I learned that it makes the module to specific. It depends on the structure of your html, but what is if the order will be changed? Use classes!

// Bad example
// Child-selector
.m-tabs > li {}

// Good example
// Classname
.m-tabs .m-tabs__trigger {}

// Better example
// Less nesting
.m-tabs__trigger {}

Avoid Magic Numbers

"Magic number" is an old school programming term for unnamed numerical constant. Basically, it's just a random number that happens to just work™ yet is not tied to any logical explanation.

Needless to say magic numbers are a plague and should be avoided at all costs. When you cannot manage to find a reasonable explanation for why a number works, add an extensive comment explaining how you got there and why you think it works. Admitting you don't know why something works is still more helpful to the next developer than them having to figure out what's going on from scratch.

// 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.
 */
.foo {
  top: 0.327em; /* 1 */
}