:
for each declaration.box-shadow
).rgb()
, rgba()
, hsl()
, hsla()
, or rect()
values. This helps differentiate multiple color values (comma, no space) from multiple property values (comma with space).#ccc
.#ccc
instead of #cccccc
.white
and black
instead of their hex counterparts: #fff
and #000
. For all other colors, use hex codes rather than color names.input[type='text']
. They’re only optional in some cases, and it’s a good practice for consistency.margin: 0;
instead of margin: 0px;
.'
) rather than double quotes ("
) in attribute selectors and properties. Do not use quotes in URI properties (url()
). Exception: the @charset
rule requires double quotation marks.Questions on the terms used here? See the syntax section of the Cascading Style Sheets article on Wikipedia.
/* Bad */
.selector, .selector-secondary, .selector[type=text] {
padding:15px; margin:0px 0px 15px;
background:rgba(0, 0, 0, 0.5) url("images/bg.png");
box-shadow:0px 1px 2px #CCC,inset 0 1px 0 #FFFFFF;
font-family:"Open Sans",Verdana,sans-serif
}
/* Good */
.selector,
.selector-secondary,
.selector[type='text'] {
margin-bottom: 15px;
padding: 15px;
background: rgba(0,0,0,0.5) url(images/bg.png);
box-shadow: 0 1px 2px #ccc, inset 0 1px 0 white;
font-family: 'Open Sans', Verdana, sans-serif;
}
Code is written and maintained by people. Ensure your code is descriptive, well commented, and approachable by others. Great code comments convey context or purpose. Do not simply reiterate a component or class name.
Be sure to write in complete sentences for larger comments and succinct phrases for general notes.
/* Bad */
/* Modal header */
.modal-header {
…
}
/* Good */
/* Wrapping element for .modal-title and .modal-close */
.modal-header {
…
}
spine-case
(not snake_case
or camelCase
)..btn
is recognizable as button, but .s
doesn't mean anything..e-*
or .js-*
to indicate an element as a target of behavior (as opposed to style). More often than not they're just doubling up on something that already has a meaningful name and only serve to make your code harder to read.These rules also apply when creating Sass and Less variable names.
/*
** Egregious. If you do this, next time
** you see me coming you better run.
*/
.margin-15 { … }
.float-right { … }
/* Bad */
.t { … }
.red { … }
/* Good */
.tweet { … }
.error { … }
<!-- Bad -->
<!-- You don't need both show-more and e-show-more -->
<a class="e-show-more show-more">Show more results…</a>
<!-- Good -->
<a class="show-more">Show more results…</a>
Related property declarations should be grouped together following the order:
Layout and positioning come first because they 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 a component's dimensions and placement.
Everything else takes place inside the component or without impacting the previous two sections, and thus they come last.
For a complete list of properties and their order, please see my stylelint-order† config.
† stylelint-order is a plugin to the popular style linter stylelint, that enables you to check the order of your style declarations.
.declaration-order {
/* Layout */
display: block;
float: left;
/* Positioning */
position: absolute;
z-index: 100;
top: 0;
right: 0;
bottom: 0;
left: 0;
/* Box model */
margin: 0;
border: 1px solid #e5e5e5;
border-radius: 5px;
padding: 20px;
width: 100px;
height: 100px;
/* Visual */
opacity: 1;
color: #333;
background: #f5f5f5 url(images/page-bg.png);
box-shadow: 0 1px 2px #ccc, inset 0 1px 0 white;
/* Typography */
font-family: 'Helvetica Neue', sans-serif;
font-size: 14px;
line-height: 1.5;
font-style: normal;
/* Content & animations */
cursor: pointer;
transition: all 0.3s linear;
text-align: left;
white-space: nowrap;
text-overflow: ellipsis;
}
Use white space (newlines) to enhance readability:
/* Bad */
.dont-do-this {
margin: 0;
border: 1px solid #e5e5e5;
.nested-block-01 {
float: left;
}
.nested-block-02 {
float: right;
margin-left: 2em;
text-align: right;
}
}
/* Good */
.this-is-better {
position: absolute;
top: 0;
right: 0;
border: 1px solid #e5e5e5;
padding: 20px;
width: 100px;
height: 100px;
.nested-block-01 {
text-align: right;
font-weight: bold;
}
.nested-block-02 {
font-size: 2em;
line-height: 1.2;
font-style: italic;
}
}
@import
Compared to <link>
s, @import
is slower, adds extra page requests, and can cause other unforeseen problems. Avoid them and instead opt for an alternate approach:
<link>
elements@import
is okay in this case)For more information, read this article by Steve Souders.
<!-- Use link elements -->
<link rel="stylesheet" href="core.css">
<!-- Avoid @imports -->
<style>
@import url(more.css);
</style>
!important
. I MEAN IT.!important
: when you need to override a style and you don't have access to the stylesheet in which it is defined, and therefore no inkling as to its specificity.!important
. It's an admission of failure, as well as the opening shot in a (thermonuclear) war of specificity against yourself.!important
.In short, don't do it.
/* !important is bad, mmmkay? */
.error {
color: red !important;
}
/*
** Instead, increase the specificity of your
** selector as necessary. This is easier to
** understand the consequences of, ergo more
** predictable.
*/
form .error {
color: red;
}
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.
.element { … }
.element-avatar { … }
.element-selected { … }
@media (min-width: 480px) {
.element { …}
.element-avatar { … }
.element-selected { … }
}
Don't use vendor prefixes in your CSS:
Instead, write your code without vendor prefixes and use a plugin like autoprefixer in your build pipeline.
/* Don't bother with prefixed properties */
.selector {
box-shadow: 0 1px 2px rgba(0,0,0,.15);
}
In instances where a rule set includes only one declaration, keep the line breaks for better readability; your declarations will begin in the same column and be easier to scan.
/* Multiple declarations, one per line */
.sprite {
display: inline-block;
width: 16px;
height: 15px;
background-image: url(../images/sprite.png);
}
/* Treat single declarations the same as multiple declarations */
.span1 {
width: 60px;
}
.span2 {
width: 140px;
}
.span3 {
width: 220px;
}
Strive to limit use of shorthand declarations to instances where you must explicitly set all the available values. Commonly overused shorthand properties include:
padding
margin
font
background
border
border-radius
Oftentimes 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.
That said, there is no denying that using shorthand properties is convenient. The rule here is to use judgement and avoid overzealously using shorthand properties, especially if you know you'll need to override them in part later.
The Mozilla Developer Network has a great article on shorthand properties for those unfamiliar with notation and behavior.
/* Bad */
.element {
margin: 0 0 10px;
background: red;
background: url(image.jpg);
border-radius: 3px 3px 0 0;
}
/* Good */
.element {
margin-bottom: 10px;
background-color: red;
background-image: url(image.jpg);
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
[class^='…']
) on commonly occurring components. Browser performance is known to be impacted by these./* Bad */
span { … }
.page-container #stream .stream-item .tweet .tweet-header .username { … }
.avatar { … }
/* Good */
.avatar { … }
.tweet-header .username { … }
.tweet .avatar { … }
Avoid unnecessary nesting. Just because you can nest doesn't mean you should. Consider nesting only if you must scope styles to a parent and if there are multiple elements to be nested. The consequence is needlessly long, less performant, overly specific selectors.
Also take care with &
, the parent selector:
&
(placing it at the end of a nested selector). It's confusing as hell and often has unintended consequences.// Don't nest when it's not necessary
.module {
.module-content {
.no-data-message {
font-size: 1.25em;
line-height: 1.5;
text-align: center;
}
}
}
// This will give you a selector that's only as long
// as it needs to be
.no-data-message {
font-size: 1.25em;
line-height: 1.5;
text-align: center;
}
// Avoid un-nesting with &
.child {
.parent & {
// This is confusing and might not behave as expected
// if .child happened to be nested in something else
}
}
// This is better and more understandable
a {
text-decoration: none;
&:hover,
&:active {
text-decoration: underline;
}
}
For improved readability, wrap all math operations in parentheses with a single space between values, variables, and operators. Take special care to do this for shorthand properties.
// Bad
.element {
margin: 10px 0 @variable*2 10px;
}
// Good
.element {
margin: 10px 0 (@variable * 2) 10px;
}
Don't ever use raw hex colors in Less and Sass. Define them with human-readable variable names and use those. Better yet, assign colors to variables indicating their semantic usage.
Using semantic color variables throughout your code is more readable and easier to change.
// Bad
.module {
border: 1px solid #eee;
}
// Better
$light-grey: #eee;
.module {
border: 1px solid $light-grey;
}
// Best
$light-blue: #def;
$light-grey: #eee;
// Now you can change this to $light-blue in one place,
// and it shows up everywhere $section-border is used. Boom!
$section-border: $light-grey;
.module {
border: 1px solid $section-border;
}
An example file hierarchy:
basics/
├── _colors.scss
├── _typography.scss
└── _grid.scss
shared/
├── _foundation.scss
├── _forms.scss
├── _mixins.scss
├── _header.scss
├── _footer.scss
└── _nav.scss
_feature-1.scss
feature-2/
├── _sub-feature-a.scss
└── _sub-feature-b.scss
_feature-3.scss
/*-----[ messaging ]----------------------------------------
*/
/*----------[ alerts ]--------------------------------------
*/
.alerts {
.message {
&.error {
…
}
&.warning {
…
}
}
}
/*----------[ growl notifications ]-------------------------
| Some additional context, if necessary.
*/
.growl {
.message {
…
}
}