Frontend Style Guide by Kaliop

CSS Guidelines

CSS syntax

  • One selector per line
  • Each declaration should appear on its own line
  • Keep { on the last selector line, one space before
  • Put } on a new line, not indented
  • Double quotes ("hello"), not single quotes ('go away')
  • Quote attribute values in selectors, e.g., input[type="text"]
  • Include one space after : for each declaration
  • Always end declarations with a semi-colon (;)
/* Bad CSS */
.selector, .selector-secondary, .selector[type=text]{
    font-weight: bold; color: black
    }
/* Good CSS */
.selector,
.selector-secondary,
.selector[type="text"] {
    font-weight: bold;
    color: black;
}

Single declarations

A rule set with a single declaration may be written on a single line like this:

.selector { property: value; }

(Space after the opening brace and before the closing brace, semi-colon at the end.)

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

/* Single declarations on one line */
.icon         { background-position: 0 0; }
.icon-home    { background-position: 0 -20px; }
.icon-account { background-position: 0 -40px; }

Indentation

Use soft tabs with 4 spaces for indentation.

  • Add one indent before each declaration.
  • Indent the contents of a media query by one tab.
  • If you wrap long values using line breaks, add one indent to each wrapped line.

Avoid adding unnecessary indentation. Do not use indentation to try to reflect the HTML structure or the hierarchy of your styles.

/* Good indentation */
.Component {
    padding: 2em;
    font-size: 18px;
}
.Component-element {
    color: black;
    text-shadow: 0 0 4px #FFF,
        0 -5px 4px #FF3,
        2px -10px 6px #FD3,
        -2px -15px 11px #F80,
        2px -25px 18px #F20;
}
@media (max-width: 500px) {
    .Component {
        padding: 1em;
        font-size: 16px;
    }
}

Property values

Dimensions

  • Use px, em or rem dimensions. Do not use cm, pt, etc.
  • Avoid specifying units when the value is zero

Colors

  • Lowercase all hex values (#30f8c4)
  • Use short hex values when possible (write #fff, not #ffffff).

 Commas

  • Comma-separated values: use a space after each comma
  • No spaces inside rgb(), rgba(), hsl(), hsla(), or rect()
/* Not ideal */
.bad-values {
    margin-top: 0px;
    margin-bottom: 0.5em;
    background-color: rgba(0, 0, 0, 0.8);
    box-shadow: 0px 1px 2px #CCC,inset 0 1px 0 #FFFFFF
}
/* Better */
.great-values {
    margin-top: 0;
    margin-bottom: 0.5em;
    background-color: rgba(0,0,0,0.8);
    box-shadow: 0 1px 2px #ccc, inset 0 1px 0 #fff;
}

Declaration order

Group related declarations together, following this order:

  1. Positioning
  2. (Display &) Box Model
  3. Text
  4. Colors, Backgrounds & Effects
  5. Other

Main idea: go from the general to the particular, from properties that have the biggest effect to properties that affect smaller details.

You don’t have to apply the exact same order for each property inside a group. Try to follow this guideline but don’t waste time trying to achieve perfect order.

.declaration-order {
    /* Positioning */
    position: absolute;
    z-index: 10;
    top: 0;
    right: 0;
    float: none;
    vertical-align: middle;
    transform: translateX(100%);

    /* (Display &) Box Model */
    content: "";
    display: inline-block;
    visibility: visible;
    opacity: .8;
    overflow: hidden;
    box-sizing: border-box;
    width: 100px;
    height: 100px;
    margin: 10px;
    border: 10px solid #333;
    padding: 10px;

    /* Text */
    white-space: nowrap;
    text-align: right;
    text-indent: 2em;
    text-overflow: ellipsis;
    text-transform: uppercase;
    font-family: sans-serif;
    font-size: 16px;
    line-height: 1.4;
    text-shadow: …;
    font-weight: bold;
    font-style: italic;

    /* Colors, Backgrounds & Effects */
    color: #fff;
    background-color: #000;
    background-image: url("…");
    box-shadow: …;
    filter: …;
    mask: …;

    /* Other */
    transition: all .5s;
    cursor: pointer;
    will-change: transform;
    -webkit-backface-visibility: …;
}

Shorthand notation

Only use shorthand declarations when you are okay with setting or resetting all the corresponding values. For instance, background: #fff; will also reset background images and positions, gradients, multiple images, etc.

Try to use individual properties, rather than shortand ones.

Commonly overused shorthand properties include:

  • padding
  • margin
  • font
  • background
  • border
/* Bad example */
.element {
    margin: 0 auto;
    padding: 0 0 10px;
    background: red;
}
/* Better example */
.element {
    margin-left: auto;
    margin-right: auto;
    padding-bottom: 10px;
    background-color: red;
}

Comments

Use comments to organize and document your CSS code.

Comments must not be in the production CSS. If you use a pre-processor, prefer the // comment syntax. Or use a minifier that removes comments.

Comments before selectors

  • When documenting why you’re using one or several lines of code, put the comment text before the selector, and put a numbered reference after each line of code the comment applies to. (See the next example.)

Identify sections of code

  • Use Markdown-like comments (e.g. /* # Level 1 heading */) to identify sections of code.
  • Always leave 2 lines before a level 1 or level 2 heading (except at the top of the page).

Sublime Text users: the Table of comments plugin can be used to list all headings in a file, and jump between headings.

//  ======================
//  # Components => Header
//  ======================

//  This is an example, using Sass comments.
//  The === decorations for level 1 headings,
//  and the --- decorations for level 2 headings,
//  are optional.
 

//  -----------------
//  ## Main container
//  -----------------

.Header {
    …
}
 

//  -------------
//  ## First line
//  -------------

.Header-top {
    …
}

// 1: We want content to align to the bottom in case one
//    is a bit higher than others, because of the "active"
//    effect that is docked to the bottom.
.Header-item {
    display: table-cell;
    vertical-align: bottom; // [1]
}

// ### Logo

…

// ### Etc.

…

Media queries

Where should I put media queries?

  • Put all styles for a website part or component in the same file.
  • Keep media queries as close as possible to the corresponding general or desktop styles.
  • Don’t create separate stylesheets for mobile styles, tablet styles, etc.

With pre-processors, it’s also possible to place media queries inside a rule set. Use this if it doesn’t create too much repetition.

Use variables

  • Define 2-3 major breakpoints for a project and store them in Sass variables. Then reuse them for most components.
  • It’s okay to use a different breakpoint in a component if you need something more specific.
  • Always write $breakpoint-variable - 1px for max-width media queries. This avoids overlapping media queries.
/* Standard media queries in CSS */
.Component { … }
.Component--selected { … }
.Component-avatar { … }

@media (min-width: 500px) {
    .Component { … }
    .Component--selected { … }
    .Component-avatar { … }
}
/* Bad - at 768px, both would apply! */
@media (min-width: 768px) { … }
@media (max-width: 768px) { … }

/* Better */
@media (min-width: 768px) { … }
@media (max-width: 767px) { … }
// Nested media queries in Sass
.Component {
    padding: 20px;
    @media (min-width: $breakpoint-medium) {
        padding: 40px;
    }
    @media (max-width: $breakpoint-small - 1px) {
        padding: 12px;
    }
}

Selectors

Keep selectors short. A selector should have 3 parts maximum (one or two is best).

  • Use classes (.something, .myClass)
  • Do not use the #id selector (its specificity is too damn high!)
  • Element selectors (h2, .MainNav a) are okay, but keep in mind that sometimes elements can change (h2 becomes a h3 or h1, p becomes a div, etc.)
  • Don’t use underscores in class names (foo_bar)
/* Bad selector: generic word :( */
.list { … }

/* Bad selector: too specific! */
.page-container #stream .stream_item .tweet .tweet_header .username { … }
/* Better selectors */
.Tweet-list { … }
.Tweet-username { … }

Components

Try to write most of your styles as components (also called modules).

A component is a group of styles that logically belong together. A component can represent a complex UI group such as the main navigation of a website, a page footer; or an less complex block such as an article teaser or a comment. Most components have several elements.

Some components can be limited to a single element. For example a Button component can be used to style all buttons accross a website (maybe with several style variations).

It can be hard to decide if some part of a design should be a big component, or several smaller components. Use your best judgement, and/or ask coworkers what they think.

Rules for components

  1. One component = one file (components/_mycomponent.scss)
  2. Classes for components and component elements (see the naming convention)
  3. Element selectors are okay if they’re scoped (.MyComponent-element a { … }); but only use them when it’s impractical or redundant to create an element class (.MyComponent-something)

Naming convention

/* Main container */
.ComponentName { … }

/* An element name is a small part of the component, such as a title, an image, a subcontainer, a button, etc. */
.ComponentName-elementName { … }

/* Both component containers and component elements can have modifiers, which modify some of the styles of their base class */
.ComponentName--modifierName { … }
.ComponentName-elementName--modifierName { … }

We follow the SUIT CSS naming conventions, but only for components. See the next sections for utility classes and JavaScript hooks and states.

/* A small component for showing a single tweet. */

.Tweet { … }
.Tweet-username { … }
.Tweet-avatar { … }
.Tweet-content { … }
/* A bigger component: this footer component has styles for the container, some text content, and a navigation. It would be possible to define the navigation as a different component (say, FooterNav). */

.Footer { … }
.Footer--compact { … }
.Footer-nav { … }
.Footer-nav li { … }
.Footer-nav a { … }
.Footer-copyright { … }
/* A Grid component that can be used to structure many parts of the site */

.Grid-group { … }
.Grid-item { … }
.Grid-item--small { … }
.Grid-item--large { … }
.Grid-item--full { … }
<!-- Two article teasers. The first one has a modifyer class so that we can make it bigger, for example. -->

<article class="Teaser Teaser--highlight">
    <h2 class="Teaser-head">
        <a href="…">…</a>
    </h2>
    <p class="Teaser-desc">…</p>
</article>
<article class="Teaser">
    <h2 class="Teaser-head">
        <a href="…">…</a>
    </h2>
    <p class="Teaser-desc">…</p>
</article>

Utility classes

Utilities are simple styles that can be used on any element to trigger some basic function (in a few lines of CSS at most).

  • Write utility classes in camelCase, without a prefix.
  • Avoid using many utility classes. They move a lot of the presentation logic into your HTML, and can hide that a component has dependencies (unlike mixins and variables).
  • If you have several utility classes that work together, maybe make a component?
/* Utility classes */
.visuallyHidden {
    position: absolute !important;
    clip: rect(1px,1px,1px,1px);
}
.svgIcon {
    display: inline-block;
    vertical-align: middle;
    width: 20px;
    height: 20px;
}

JavaScript hooks and states

Use a component’s existing classes for selecting elements with JavaScript. If you can’t use an existing component class, it might be a good idea to target an id, or a data-attribute.

Avoid writing styles with JavaScript. Instead, change a class name or an attribute:

  • Use native attributes, and selectors such as :selected, when possible.
  • If you want to hide an element so that it can’t be interacted with or read (until the user clicks a button, for instance), use aria-hidden="true". Then show elements with aria-hidden="false". You will need to write your own CSS.
  • If using aria-hidden doesn’t make sense, consider using a data-attribute, or add a simple class to the element.
// Selecting elements using existing component classes
var container = document.querySelector('.MyComponent');
var button = container.querySelector('.MyComponent-toggle');
var dropdown = container.querySelector('.MyComponent-details');

// Set a state using attributes
container.setAttribute('data-state', 'something');
dropdown.setAttribute('aria-hidden', 'true');

// Set a state by adding a class
button.classList.add('selected');
/* Styling different states with CSS */
.MyComponent-item[aria-hidden="true"] {
    display: none;
}

/* Or with a simple class */
.MyComponent-item.hidden {
    display: none;
}

/* Custom attribute/values can be useful */
.Header[data-state="sticky"] {
    …
}

Preprocessors

Use the Sass preprocessor, with the SCSS syntax. (Previous Kaliop projects used LESS.)

Sass files should have a .scss extension, and “partials” (files that you import in another file) should start with an underscore, e.g. _reset.scss.

Sass and LESS source files can be compiled to CSS:

  1. with a desktop client (Koala is alright);
  2. with a node-based frontend build system (e.g. Brunch, Gulp);
  3. with a build process integrated in the framework (e.g. with Assetic filters).

Variables and mixins

Use lowercase names, and follow an general-to-particular progression:

  • Global variables and mixins: $type-name, @mixin some-name
  • If specific to a component: $component-type-name, @mixin component-some-name
// base/_variables.scss
$color-brand:       #e95d1e;
$color-link:        #08c;
$breakpoint-small:  500px;
$breakpoint-medium: 860px;
// base/_grid.scss
@mixin grid-container(…) { … }
@mixin grid-item(…) { … }
// component/_header.scss
$header-color-bg: #555 !default;
$header-color-link-main: #fff !default;
$header-color-link-active: #faa !default;

Selector nesting

Selector nesting makes your code more difficult to read. Avoid unnecessary nesting.

  • Nest once (1 level deep), twice (2 levels deep) if you must, never more.
  • Keep the resulting CSS selectors as short as possible.
  • Do not overuse the & symbol.
// BAD: unnecessary nesting

.Header {
    .Header-top {
        .Header-logo { … }
    }

.Nav {
    ul {
        li {
            a { … }
        }
    }
}
// GOOD: avoid nesting

.Header { … }
.Header-top { … }
.Header-logo { … }

.Nav {
    ul { … }
    li { … }
    a { … }
}

Organization

Organize your CSS code in multiple files. In particular, each component should be one file (even if it’s only 20 lines of code).

  • Try to keep files under a maximum of 500 lines.
  • Do not put two components in the same file.
  • Concatenate before serving to the browser (preprocessors give you “free” concatenation).