This document outlines CSS/LESS best practices at WebMD Health Services. For thr entire front-end development guidelines, see this document.
The second component of a web page is the presentation information contained in the Cascading Style Sheet (CSS). Web browsers successful implementation of CSS has given web authors site-wide control over the look and feel of their web sites.
Just as the information on a web page is semantically described in the HTML Markup, CSS describes all presentation aspects of the page via a description of its visual properties. CSS is powerful in that these properties are mixed and matched via identifiers to control the page's layout and visual characteristics through the layering of style rules (the "cascade").
We use the .LESS CSS preprocessor to aid our CSS generation. Care should be taken when writing css .LESS makes it easy to nest selectors that compile into long selectors. Long selectors chains can cause file bloat, selector precedence problems, adversely affect problems and make css harder to maintain and edit.
Rule of thumb
- Avoid creating selectors with more than 2 spaces in them
- Avoid
!important
!important
is a sign that we have a selector war and other selectors have too high of specificity, try to refactor the css instead of adding !important
.
Get to know CSS Selectors.
Embrace CSS3 to progressively enhance styles for newer browsers.
We aren't all CSS experts, reach out to one of our many knowledgable CSS devs before hacking together a solution in JavaScript.
You should include exactly one stylesheet on your page and no more. This base file should then import all dependancies and components. Components and styles for different sections should be contained in their own stylesheet and should not cross-pollinate. The home page styles directory, for example, contains the files
styles
|-- homepage.less
|-- _layout.less
|-- _featuredNews.less
|-- _featuredVideo.less
|-- _promoWeblets.less
homepage.less
is included on the page and imports the different files for each section like this:
@import '/themes/common/css/minimal';
@import 'layout';
@import 'featuredNews';
@import 'featuredVideo';
@import 'promoWeblets';
And that's it. minimal.less
and the legacy-filled theme_builder.less
import site chrome like the header, footer, nav, and other junk you don't have to worry about unless you're Obelisk. One of these files must be included. The master themes_common.less
and a sponsor's theme.less
are magically included in every stylesheet by default and import our Pattern Library components, so you can utilize our global variables and mixins in any LESS file on the site.
Our Pattern Library is broken up into three pieces, core
, components
, and modules
.
Core contains base styles for our site's body, header, footer, and navigation. It also handles a lot of the global responsive bits. Anything between the nav and the footer should not use anything from core.
Components describe define things like forms, buttons, tables, sprites, grids, etc, that are used frequently across the site. All of these components are organized into mixins and included for you so you can call them at any time, but some have special instructions. Check the documentation.
Modules are more specialized components like modals, tab panels, and progress bars. Since these components are used infrequently and tend to be more customizable they live here.
There is one more piece of the Pattern Library that contains legacy/deprecated components, and you should stay away from it unless you're Obelisk.
BEM is an object-oriented methodology for organizing resuable blocks of mark-up and CSS. Many of our pattern library components are being ported to BEM. It allows us to rapidly prototype and redesign components because they are not coupled to each other.
.block {}
.block__element {}
.block--modifier {}
BEM is very easy to wrap your head around, and has helped reduce CSS bloat casued by nesting, overrides, and repetitive mixins. We reccomend you adopt this convention when building applications. Get to know BEM syntax.
Use common variables for things like colors and gutters. This way clients can override variables with their branding colors and fonts if need be. All global variables can all be located in themes_common.less
, but the ones we recommend using are in the Pattern Library.
Here's a good example of how to use theme variables wisely.
.class {
color: @brand_Primary;
background: @gray_Lighter;
border: 1px solid @gray_Light;
}
We want to make sure our LESS looks familiar to everyone editing it. That's why these guidelines are not simply suggestions, but the de facto style you should be coding. The Front-End Community of Practice decides on the style based on our standards. Don't get busted by the COPs.
Indent four spaces, no tabs
Class and ID names should be camelCase
.className {...}
#theIdName {...}
HTML elements should be lowercase, of course. That said, avoid styling naked tags.
div {...}
span {...}
Put a space between selector and opening bracket and put the closing bracket on a separate, non-indented line
.class {
color: #000;
}
Return after each closing bracket
.class1 {
color: #000;
}
.class1 {
color: #000;
}
Put multiple selectors on separate lines, separated by commas
.class1,
.class2,
.class3,
.class4 {
color: #000;
}
Make sure there is a space between property and value, each property is indented one level, and end each line with a semi-colon ;
div {
color: #000;
padding: 16px;
height: 100px;
}
Specify units (px, em, %
) unless it is 0
(then omit the units).
padding: 10px 0 15px;
Specify HEX values for colors
#000
#FF0138
Use LESS functions to adjust hue, saturation, lightness, or transparency
color: fade(#000, 50%);
background: mix(#FFF, #000, 50%);
Strings must use single-quotes
a:after {
content: 'pizza';
}
CSS3 properties that support layering should be on separate lines, with the semi-colon on the last line
.class {
box-shadow:
0 2px 4px #000,
0 4px 8px #333;
color: #000;
}
In LESS, variables are declared with @
preceding the name.
@themeColor: #F00;
Any color or layout value used more than once should be a variable
@alertColor: #0F0;
@columnWidth: 80px;
Any variable that is available for client customization must be declared globally in themes_common
Always add parenthesis to declare mixins. Mixins should be styled like selectors. Do not put a space after the mixin name.
.mixin() {
color: #F00;
}
Include mixins first when declaring mixins inside selectors
div {
.mixin();
padding: 16px;
}
Define mixins at the bottom of the document or in their own separate file via @import
div {
.mixin();
}
...
.mixin() {
color: #F00;
}
Do not put spaces around parameters
.mixin(@color) {
background: @color;
}
Specify default parameters whenever possible.
.mixin(@color: #F00) {
background: @color;
}
When calling mixins, you do not need to specify the parameter name, just the value
.class {
.mixin(#F00, 100px);
}
Multiple parameters with values should be separated on their own lines.
.mixin(
@color: #F00,
@width: 100px
) {
background: @color;
width: @width;
}
Guarded mixins with boolean values that are true do not need = true
.mixin(@boolean) when (@boolean) {...}
Use the when not
keyword when specifying falsey booleans
.mixin(@boolean) when not (@boolean) {...}
Use bundles to contain similar mixins
#gradient {
.linear() {...}
.radial() {...}
}
Call the bundles with the greater than >
character
div {
#gradient > .linear(...);
}
Make sure you're not redefining mixins that exist globally! Check components > common.less
- Don't use inline styles
- Use semantically descriptive class names (
.orangeButton
is terrible) - Never nest deeper than three levels (The Inception Rule)
- Never nest an ID within and ID
- Avoid name spacing with element + class names unless it's a state rule (i.e.,
div.class
) - Don't repeat yourself (DRY), abstract patterns into reusable mixins and variables
- Use shorthand whenever possible (
margin
instead ofmargin-top
+margin-right
, etc.) - group properties together (layout, fonts, background, prefixes, hacks)
- Use the star-hack
*property: value;
to easily target IE7 - When importing LESS files, do not put .less extension (
@import 'component'
) - If your LESS file is over 100 lines it's time to start breaking things apart
- If you're doing something dumb, document why with a
//
comment