Table of Contents

Any break of these laws should be sufficient enough to turn down a pull request!

Introduction

CSS is a very powerful but dangerous weapon. On one hand, it allows you to build extraordinary things, but on the other hand it can have an awful impact on your app’s scalability and maintainability if not handled carefully.

There are a lot of bad habits that can damage your stylesheets. Things like overwriting specific rules multiple times, using “magic numbers” or overqualified selectors are known as “code smells” and are signs of unhealthy CSS.

The fact that CSS is a highly opinionated language and that it applies on a global scope, makes it hard for developers to collaborate and keep a scalable and maintainable codebase without a set a guides and rules.

Syntax and Formatting

Single declarations

In instances where a rule set includes only one declaration, consider removing line breaks for readability and faster editing. Any rule set with multiple declarations should be split to separate lines.

.selector { display: block; }

The key factor here is error detection—e.g., a CSS validator stating you have a syntax error on Line 183. With a single declaration, there's no missing it. With multiple declarations, separate lines is a must for your sanity.

Declaration order

We group by type within that scope we toggle between arbitrary order and order by line length. Types are ordered by their importance, from loose to more specific properties.

.element {
    @extend %font-size-16;
    @extend %clearfix;

    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1;

    display: flex;
    flex: 1 1 auto;
    height: auto;
    @import width-full(); /* width: 100%; */

    border: 1px solid #fff;
    border-radius: 5px;

    color: #cecece;
    background-color: #000;

    font-family: sans-serif;
    font-size: 100%;
    font-style: regular;
    text-align: center;
    text-transform: uppercase;

    opacity: 1;
    transform: none;
    pointer-events: auto;
}

@todo we should have a reference to a .csscomb.json file and explain the reasoning behind it

https://github.com/pixelgrade/components/blob/master/.csscomb.json

Shorthand notation

@todo maybe use the precss postcss plugin that allows you to write things like margin: * auto; to avoid accidentally overwriting unnecessary values.

Strive to limit use of shorthand declarations to instances where you must explicitly set all the available values. Common overused shorthand properties include:

.selector {
    /* margin: 0 auto; */
    margin-left: auto;
    margin-right: auto;
}
Often times we don't need to set all the values a shorthand property represents. For example, HTML headings only set top and bottom margin, so when necessary, only override those two values. Excessive use of shorthand properties often leads to sloppier code with unnecessary overrides and unintended side effects.

Commenting

Double slash // comments should be used for comment blocks in SCSS(instead of /* */). A notable exception would be in _main.scss described above.

/* Comment in SCSS - valid but not encouraged */
// Comment in SCSS - valid and encouraged 

/* Comment in CSS */

_main.scss should have a table of contents. The table of contents will have CSS comments /* */ so that it will be output in the CSS file. All .scss files from all folders will be included in _main.scss.

Example: _main.scss

/**
 * Table of Contents
 *
 * 1.0 - Reset
 * 2.0 - Base Styles
 * 3.0 - Abstractions
 *   3.1 - Grid
 *   3.2 - Media
 * 4.0 - Components
 *   4.1 - Header
 *   4.2 - Navigation
 *   4.3 - Footer
 *   4.4 - Card
 *   4.5 - Button
 *   4.6 - Small Links
 *   4.7 - Intro
 *   4.8 - Dropcap
 *   4.9 - Search Form
 *   4.10 - Comments Area
 *   4.11 - Tags
 *   4.12 - Post Navigation Links
 *   4.13 - Widgets
 *   4.14 - Infinite Scroll
 * 5.0 - Page Specific Styles
 *   5.1 - Singular
 * 6.0 - Overwrites
 */

// Settings
@import "settings/default";

// Tools
@import "tools/clearfix";
@import "tools/helpers";
@import "tools/queries";
@import "tools/easings";
@import "tools/font-face";
@import "tools/wp-offset";



/**
 * 1.0 - Reset
 */
@import "generic/reset";
@import "generic/box-sizing";



/**
 * 2.0 - Base Styles
 */
@import "base/typography";
@import "base/blockquote";
@import "base/pre";
@import "base/tables";
@import "base/titles";
@import "base/form-elements";
@import "base/wp-align";



/**
 * 3.0 - Abstractions
 *   3.1 - Grid
 */
@import "objects/grid";

/**
 *   3.2 - Media
 */
@import "objects/media";

(...)

_main.scss will be included in style.scss that contains holds CSS comments with pieces of information(e.g. WordPress theme version) that will appear as comments in the generated style.css file.

Example: style.scss


@charset "UTF-8";
/*
Theme Name: Hive
Theme URI: http://pixelgrade.com/demos/hive/
Author: Pixelgrade
Author URI: http://pixelgrade.com
Description: An effortless tool for publishers of all kind, cherished for its clean masonry-style layout (...)
Version: 1.2.5-wpcom
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: hive_txtd
Domain Path: /languages/
Tags: blog, bright, clean, custom-menu, editor-style, elegant, fashion, featured-images, geometric (...)

This theme, like WordPress, is licensed under the GPL.
Use it to make something cool, have fun, and share what you've learned with others.

Hive is based on Underscores http://underscores.me/, (C) 2012-2014 Automattic, Inc.
*/

@import "main";
	

Architecture

The Inverted Triangle CSS Methodology is a scalable and maintainable CSS architecture introduced by Harry Roberts which gives you a set of rules to help you deal with CSS specifics like global namespace, cascade and selector specificity.

One of the key principles of ITCSS is that it separates your CSS codebase to several sections (called layers), which take form of the inverted triangle:

Folder Structure

|-- base
|-- components
|-- generic
|-- tools
    |-- _mixins.scss
|-- objects
|-- trumps
|-- vendor
|-- _main.scss
|-- _settings.scss
|-- _shame.scss
|-- style.scss

Given the WordPress environment in which we do our work, most times we will include in our project third-party plugins with their own CSS. These files should reside in the vendor folder and the place in the Inverted Triangle would theoretically be the Components layer.

Naming conventions

In no particular order, here are the individual namespaces and a brief description. We’ll look at each in more detail in a moment, but the following list should acquaint you with the kinds of thing we’re hoping to achieve.

Objects: o-

Signify that the element which it is applied to is an Object. It can be used for layout purposes or abstracting visual patterns. These elements’ properties shouldn’t be modified by other elements or based on context. Modifying the object itself should be avoided.

Components: c-

Components are well-defined pieces of UI and their properties are mostly for decoration purposes. The scope to which changes to these elements’ styling would apply is easily to detect and explain.

Utility: u-

Utilities classes are in no way attached to any piece of ui or code. Each class has a specific purpose and they come from methodologies like Tachyons / Basscss / Atomic CSS. They are more suited when developing for an environment where you have full control over the markup but that’s not the case for WordPress.

State: is-, has-

State classes define a scope which each elements are styled in a certain way cause of the current state of the application. They are usually temporary or optional

JavaSript Hooks: js-

These classes don’t have any stylistic purposes and are used to bind JavaScript to them for a certain behavior.

CSS Selectors

The absence of scope in CSS is one of the main reasons code gets bad. Nesting is not an option since it brings unwanted specificity to your selectors. This leaves us with the only option of using a naming convention for the class names used.

BEM

http://getbem.com/naming/

BEMIT

http://csswizardry.com/2015/08/bemit-taking-the-bem-naming-convention-a-step-further/

An ID / class should be as short as possible but as long as necessary

Dealing with specificity

Selecting IDs

<div id="widget">
	...
</div>

Naturally, given that we can’t edit this HTML to use a class instead of (or alongside) the ID, we’d opt for something like this:

#widget {
    ...
}

Now we have an ID in our CSS, and that is not a good precedent to set. Instead, we should do something like this:

[id="widget"] {
    ...
}

This is an attribute selector. This is not selecting on an ID per se, but on an element with an attribute of id which also has a certain value.

The beauty of this selector is that it has the exact same specificity as a class, so we’re selecting a chunk of the DOM based on an ID, but never actually increasing our specificity beyond that of our classes that we’re making liberal use of elsewhere.

Just because we know a way of using IDs without introducing their heightened specificity, it does not mean we should go back to using IDs in our CSS; they still have the problem of not being reusable. Only use this technique when you have no other option, and you cannot replace an ID in some markup with a class.

Maybe it's not that !important

!important should be avoided at all costs. There are certain cases where !important can or should be used. For example utility classes and / or when you want to overwrite some inline CSS which for some reason cannot be removed. In this case there should be a CSS comment above the line where !important is used to explain the reason it was used.

Safely increasing specificity

You can chain a selector with itself to increase its specificity. That is to say .btn.btn {} will apply to the same elements as .btn {} but with double the specificity. We can take this as far as we need to: .btn.btn.btn.btn {} but hopefully we’ll never get that far.

Closing Remarks

Mixins and @extend

Mixins are powerful tools which can be used to leverage your work and also handy shortcuts. The most valuable use of mixins is generating CSS. It is very helpful for having DRY code and changes in different modules come with less effort. We should at most times try to keep the declaration and usage of mixins inside the same files so we don’t create any unneeded dependencies. The same applies for @extend which must not be used inside components! Components should be independent chunks of code with high portability. Also using @extend proves to be less performant than using mixins (probably even repeating code) when the source is gzipped. (see http://csswizardry.com/2016/02/mixins-better-for-performance/)

Keep your codebase clean

  • Organize sections of code by component.
  • Develop a consistent commenting hierarchy.
  • Use consistent white space to your advantage when separating sections of code for scanning larger documents.
  • When using multiple CSS files, break them down by component instead of page. Pages can be rearranged and components moved.