CSS style guide
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
LICENSE
README.md

README.md

CSS style guide

Table of Contents

Introduction

The CSS style guide in use at Procurios is based on various sources and experiences in the past few years. Most of it is based on our own experience with maintaining a large codebase. An important source of inspiration is Harry Roberts, who published his work on cssguidelin.es. Another inspiring source is Code Guide, by @mdo.

Rules (anatomy and syntax)

A CSS rule is built out of the following blocks:

[selector] {
	[property]: [value];
}

The combination of a property and a value is called a declaration. A typical CSS rule based on this style guide looks as following:

.fooBlock {
	display: block;
	text-align: center;
	color: #fff;
}

MDN has a great piece on CSS syntax if you want to learn more.

Place each selector on its own (new) line

/** bad */
.fooBlock, .fooBlock--collapsed {
	display: block;
}

/** good */
.fooBlock,
.fooBlock--collapsed {
	display: block;
}

↑ back to top

Place a property-value pair on the same line

/** bad */
.fooBlock {
	display:
		block;
}

/** good */
.fooBlock {
	display: block;
}

↑ back to top

Place each declaration on its own (new) line

/** bad */
.fooBlock {
	display: block; float: left;
}

/** good */
.fooBlock {
	display: block;
	float: left;
}

↑ back to top

End each declaration with a semicolon

/** bad */
.fooBlock {
	display: block;
	float: left
}

/** good */
.fooBlock {
	display: block;
	float: left;
}

↑ back to top

Declaration order: positioning > box model > typographic > visual

Related property declarations should be grouped together following the order:

  1. Positioning
  2. Box model
  3. Typographic
  4. Visual

Positioning comes first because it can remove an element from the normal flow of the document and override box model related styles. The box model comes next as it dictates dimensions and placement.

Everything else takes place inside the component or without impacting the previous two sections, and thus they come last.

For example:

.declarationOrder {
	position: absolute;
	top: 100px;
	left: 100px;
	width: 1000px;
	height: 1000px;
	border: 1px solid #e5e5e5;
	font: normal 13px "Helvetica Neue", sans-serif;
	color: #444;
	background: #f5f5f5;
}

↑ back to top

Whitespace

Use tabs to indent declarations

/** bad */
.fooBlock {
∙∙∙∙display: block;
}

.fooBlock {
∙∙display: block;
}

/** good */
.fooBlock {
	display: block;
}

↑ back to top

Place a space before the opening brace

/** bad */
.fooBlock{
	display: block;
}

/** good */
.fooBlock {
	display: block;
}

↑ back to top

Place the closing bracket on its own (new) line

/** bad */
.fooBlock {	display: block; }

.fooBlock {
	display: block; }

/** good */
.fooBlock {
	display: block;
}

↑ back to top

Place a space after the property-value delimiting colon

/** bad */
.fooBlock {
	display:block;
}

/** good */
.fooBlock {
	display: block;
}

↑ back to top

Place the first declaration on a new line after the opening brace

/** bad */
.fooBlock {	display: block;
	color: #fff;
}

/** good */
.fooBlock {
	display: block;
	color: #fff;
}

↑ back to top

Limit line length to 80 characters

Where possible, limit the length of a line to a maximum of 80 characters.

/**
 * This comment explains the following CSS in detail. A little bit too many
 * details, because the length of one line exceeds 120 characters. Hence,
 * it's carefully broken into pieces to enhance readability.
 */

There will be unavoidable exceptions to this rule (fe. URLs, gradient syntax).

↑ back to top

Indenting

Don't indent related rules / rulesets

Our naming convention should be sufficient to visualize relations between rules.

/** bad */
.fooBlock {
	...
}

	.fooBlock__bar {
		...
	}

		.fooBlock__baz {

		}

/** good */
.fooBlock {
	...
}

.fooBlock__bar {
	...
}

.fooBlock__baz {

}

↑ back to top

Comments

Don't describe the obvious

We use comments in our CSS files for two purposes:

  • Clarification of the document structure.
  • Explanation of a specific declaration or rule.

There is no such thing as too much commenting -- as long as a comment improves the maintainability or interchangeability.

↑ back to top

Use sectioning comments if 1 CSS file contains multiple sections

/**
 * This is a sectioning comment and it explains the following CSS in detail.
 */

Please note that multiple sections in 1 file might be a code smell. Consider splitting discrete chunks of code into their own files.

↑ back to top

Use inline comments to explain a specific rule or declaration

/** This rule needs some explanation */
.fooBlock {
	overflow: hidden;
}

.fooBlock {
	overflow: hidden; /** This declaration needs some explanation */
}

↑ back to top

Naming conventions

We use a BEM like naming convention. Read more about it in our HTML style guide repository.

Selectors

I quote Harry Roberts:

Poor Selector Intent is one of the biggest reasons for headaches on CSS projects. Writing rules that are far too greedy—and that apply very specific treatments via very far reaching selectors—causes unexpected side effects and leads to very tangled stylesheets, with selectors overstepping their intentions and impacting and interfering with otherwise unrelated rulesets.

CSS cannot be encapsulated, it is inherently leaky, but we can mitigate some of these effects by not writing such globally-operating selectors: your selectors should be as explicit and well reasoned as your reason for wanting to select something.

Use classes to select elements

The reasoning behind using only classnames to select elements:

  • The CSS is decoupled: The dependency on the DOM is kept to a minimum. It makes it much easier to move your CSS around.
  • The CSS is isolated: Classnames based on our naming convention are scoped to a specific Block.
  • The CSS is descriptive: Classnames based on our naming convention tell developers stuff about what they're styling.
/** bad */
header ul {
	...
}

/** good */
.siteNav {
	...
}

Don't worry about unavoidable exceptions to this rule. Use it as a rule of thumb.

↑ back to top

Aim to write reusable classes

Reusable classes can be moved, recycled and duplicated across projects. They are location independent and can be reused an infinite amount of times.

/** bad */
.metaData dd {
	font-weight: bold;
}

/** good */
.metaData__label {
	font-weight: bold;
}

↑ back to top

Performance

Performance of CSS selectors is more interesting than it is important with today's browsers. If you want to know more about selector parsing, start here.

↑ back to top

Specificity

Don't use IDs

Not only are IDs non-reusable, they are also vastly more specific than any other selector, and therefore become specificity anomalies. Learn more about using IDs in CSS selectors here.

↑ back to top

Use the least number of selectors required to style an element

/** bad */
.fooBlock > .fooBlock__title {
	color: hotpink;
}

/** good */
.fooBlock__title {
	color: hotpink;
}

↑ back to top

Use !important with care

The general rule is that !important is always a bad thing, but all rules have exceptions. Harry Roberts makes a distinction between proactive and reactive use of !important.

Proactive use of !important is when it is used before you’ve encountered any specificity problems; when it is used as a guarantee rather than as a fix. For example:

.one-half {
    width: 50% !important;
}

Reactive use of !important is when it is used to combat specificity problems. In these situations, it is preferable that you investigate and refactor any offending rulesets to try and bring specificity down across the board.

↑ back to top

Media queries

Place media queries as close to their relevant rule sets whenever possible. Don't bundle them all in a separate stylesheet or at the end of the document. Doing so only makes it easier for folks to miss them in the future. Here's a typical setup.

.fooBlock {
	...
}

.fooBlock__avatar {
	...
}

@media (min-width: 480px) {
	.fooBlock {
		...
	}

	.fooBlock__avatar {
		...
	}
}

↑ back to top

Element queries

An "element query" is like a media query, specifically the 'width'/'height' MQs, but they apply to the width/height of the element's container, rather than of the screen or device.

EQs aren't implemented by browsers yet. Tab Atkins explains why EQs are difficult:

Element Queries (let's just call them EQs from here on) suffer from basic circular dependency issues.

Here's a simple example: Say you have an element which is normally 100px wide, but it uses an EQ to say that if its container is less than 200px wide, the element becomes 500px wide. (This is silly, but bear with me.) If the container is explicitly sized (width: 150px; or the like), this is fine, but if the container is sized to its contents (float: left;, for example), then you have a circular dependency: if the element is 100px wide, the container is 100px wide, but that trigger the EQ, so the element is 500px wide, so the container is 500px wide, but that disables the EQ, so the element is 100px wide, so the container is 100px wide, etc.

In other words: browser vendors have some fundamental issues to deal with before EQs are shipped as native feature. Tab ends his overview with:

So that's the state of Element Queries in 2014. We're not really any closer to having them than we were in 2013, but there's light on the horizon for a possible good solution.

That said, EQs are essential to create independant, isolated UI components. We've published a Javascript library that takes care of this issue. The library contains a comprehensive README with clear instructions on how to implement and use element queries.