Coding standards for stylesheets
Audience: non-CSS developers writing HTML.
Related: Live Style Guide (Components) | Wiki (Concepts)
The purpose of this document is to provide guidelines for writing CSS. Code conventions are important for the long-term maintainability of code. Most of the time, developers are maintaining code, either their own or someone else’s. The goal is to have everyone’s code look the same, which allows any developer to easily work on another developer’s code.
Table of Contents:
- Coding Style
- Indentation
- Use Hyphens between words in a classname
- Brace Alignment
- Selectors
- Properties
- No Vendor-Prefixed Properties
- Using !important
- Separate multiple classes with TWO spaces for readability
- Units
- HEX values
- String Literals
- URLs
- Attribute values in selectors
- JavaScript and Test Dependence
- :hover and :focus
- Comments
- Order of classes in HTML
- Adding New Feature-specific Styles
- Style Documentation
Each indentation level is made up of two spaces. Do not use tabs. (Please set your editor to use two spaces.)
/* Good */
.c-message {
color: #fff;
background-color: #000;
}
/* Bad - all on one line */
.c-message {color: #fff; background-color: #000;}
In the case of single selector, single property rules, one line is acceptable. Leave one space inside the brackets.
/* Fine - one selector, one property */
.c-alert { font-weight: bold; }
Rules inside of @media
must be indented an additional level.
/* Good */
@media screen and (max-width:480px) {
.c-message {
color: green;
}
}
/* Good */
.c-data-table {}
/* Bad - uses single underscore. */
.c_data_table {}
/* Bad - uses camel-case. */
.c-listInlineBoxy {}
The opening brace should be on the same line as the last selector in the rule and should be preceded by a space. The closing brace should be on its own line after the last property and be indented to the same level as the line with the opening brace.
/* Good */
.c-description {
color: #fff;
}
/* Bad - closing brace is in the wrong place */
.c-description {
color: #fff;
}
/* Bad - opening brace missing space */
.c-description{
color: #fff;
}
Each selector should appear on its own line. The line should break immediately after the comma. Each selector should be aligned to the same left column.
/* Good */
button,
input.c-button {
color: red;
}
/* Bad - selectors on one line */
button, input.c-button {
color: red;
}
-
Type selectors (addressing elements by their tag name) should be defined early in the cascade and then never used again unless absolutely necessary.
-
Selectors consisting of a single class should make up the majority of your CSS. A second class should generally only be added for states.
-
NEVER use id's in selectors. They have very high specificity compared to classes, and can't be used more than once on the same page.
-
For the same reason, NEVER use inline styles. They are near-impossible to override without resorting to JavaScript.
-
It should go without saying, don't use
!important
except in rare cases:- Utilities use it to override any previous values.
- Overriding any high-specificity styles we don't have control over.
Keep combinators (whitespace, >
, +
, ~
,) to a minimum. Favor direct-child combinator (>
) over descendant (whitespace).
.c-super-table a {...}
can get you in trouble later when you want to override that link style using .special
-- better to add a .c-super-table__link
in the first example to keep both depth of applicability and specificity low. A single class selector can always be overridden by a single class selector later in the cascade.
Each property must be on its own line and indented one level from the selector. There should be no space before the colon and one space after. All properties must end with a semicolon.
/* Good */
.c-story {
background-color: blue;
color: red;
}
/* Bad - missing spaces after colons */
.c-story {
background-color:blue;
color:red;
}
/* Bad - missing last semicolon */
.c-story {
background-color: blue;
color:red
}
Don't use vendor prefixes like -webkit-border-radius
, just use the unprefixed form. Autoprefix will be run on all the styles when they are compiled, and necessary prefixed properties will be added to the final output CSS automatically.
/* Good */
.c-directory {
border-radius: 4px;
}
/* Bad */
.c-directory {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
Suffix property value pairs that apply only to a particular browser or class of browsers with a comment listing browsers affected.
background: #fcfcfc; /* Old browsers */
background: url('fills/texture-1.jpg') /* IE 8- */
background: url('fills/texture-1.jpg'), url('fills/texture-2.png'); /* W3C */
Suffix fallback with “Old browsers” and standard property with “W3C”. Add a plus or minus to indicate that a property applies to all previous browsers by the same vendor or all future browsers by the same vendor.
Do not use !important on CSS properties. The only time this is allowed is in a u- utility style (provided by Core team).
/* Good */
.c-story__heading {
color: red;
}
/* Bad - don't use !important */
.c-story__heading {
color: red !important;
}
<!-- Good -->
<div class="c-inline-list c-tutorial-nav u-pull-left">
<!-- Bad - don't use single space -->
<div class="c-inline-list c-tutorial-nav u-pull-left">
<!-- Bad - not ordered by object inheritance -->
<div class="c-tutorial-nav u-pull-left o-inline-list">
In general, use mod() -- this is a custom SASS function that provides measures relative to a common standard. The value of mod(1) is 1rem. Rems are like ems, but without cascading problems: if a rem = 16px, you can count on that value anywhere in the page.
In order to keep things in sensible proportions, as well as improve readability, write mod values in whole numbers or simple fractions.
Put one space on either side of an operator in an expression.
/* Good */
margin: mod(1);
padding: mod(1 / 2);
/* Bad: uses rems directly */
margin: 0.75rem;
padding: 1rem;
/* Bad: uses fractions unrelated to the base unit,
and no spaces around operator: */
padding: mod(3/5);
mod() should be used for all margins, padding, gutters, font sizes, etc.
Pixel lengths are discouraged, but not blanket verboten! For little tweaky things like box-shadow offsets or margin: -1px
, they are fine.
Ems should be used very rarely -- example: if you need to add a bit of simulated small-caps inside a heading. We don't care which heading level it is, so we specify it relative to the parent with ems. You wouldn't want to use them on an element that has further levels of hierarchy.
Zero values do not require named units, omit the “px” or other unit.
/* Good */
.c-story {
margin: 0;
}
/* Bad - uses units */
.c-story {
margin: 0px;
}
When specifying color values in HEX, use lowercase, and if possible, 3-character shorthand:
/* Good */
.c-story__credit {
color: #ccc;
background-color: #1b48fa;
}
/* Bad */
.c-story__credit {
color: #CCCCCC;
background-color: #1B48FA;
}
Strings should always use single quotes (never double quotes).
/* Good: single quotes */
.c-story__credit:after {
content: 'Stubbornella';
background: transparent url('beachball.png');
}
/* Bad - double quotes */
.c-story__credit:after {
content: "Stubbornella";
background: transparent url("beachball.png");
}
When using a url() value, always use single quotes around the actual URL.
/* Good */
.c-header {
background: url('img/logo.png');
}
/* Bad - double quotes */
.c-header {
background: url("img/logo.png");
}
/* Bad - missing quotes */
.c-header {
background: url(img/logo.png);
}
Use double quotes around attribute values.
/* Good */
input[type="submit"] {}
/* Bad - using single quote */
input[type='submit'] {}
/* Bad - missing quotes */
input[type=submit] {}
If an item is manipulated by Javascript, it should be have a js- class purposes of selecting that element. Javascript should only query elements by js- classes. CSS should not use js- classnames in its selectors.
<!-- Good -->
<div class="c-tabset js-tabset"></div>
<script> tabset = $('.js-tabset'); </script>
<!-- Bad: -->
<div class="c-tabset"></div>
<script> tabset = $('.c-tabset'); </script>
Likewise, automated tests need a test- class prefix and should select only by that classname, never by styling classes or js- classes.
If :hover pseudo class is styled, :focus should also be styled for accessibility. Focus styles should never be removed entirely from any focusable element.
/* Good */
a:hover,
a:focus {
color: green;
}
/* Bad - missing :focus */
a:hover {
color: green;
}
/* ==========================================================================
Section comment block
========================================================================== */
/*
Sub-section comment block
*/
/* Basic one-line comment */
Classes should generally go in this order:
Base class > modifiers > states > utilities > js hooks > test hooks
<div class="c-strand c-strand--toc">
<div class="c-flashcard c-flashcard--speech-rec is-flipped">
<div class="c-module c-module--dashboard is-selected u-pad-0 js-module test-calendar">
If you need to add new styles, put them in the /features directory in the music gem. Create a subfolder for your feature if one doesn't exist.
-
Feature style classnames adhere to the same guidelines as any others, just make sure to NEVER give a feature class the same name as a library class (Note: the
f-
prefix has been deprecated). -
Adding a modifier to a class that exists in the library is encouraged: e.g., if
c-module
exists in the library, you can add a style in your "Gradebook v2" feature that definesc-module--grade-detail
, provided that class does not already exist in the library. -
Scope your styles by adding a feature namespace class like "ns-gradebook-v2" and prefix all your selectors with it. This class should go on the
<body>
.- Example:
.ns-gradebook-v2 .c-student-name { ... }
- Example:
The way feature styles are included depends on which CSS library is being used:
-
For features using the existing pre-v1 CSS and layouts: Reference your feature stylesheet in your views.
-
For new music v1 features only: All feature stylesheets are included in the main stylesheet, so there will no longer be a need to import them on a per view basis.
We use multiple strategies to make sure the CSS library is understandable and maintainable:
- SMACSS prefixes indicate different types of objects.
- BEM classnames make structure clear on both the HTML and CSS sides.
- Components and other classes are documented in specially formatted comments directly in the CSS/SASS files (see sample below).
- We use Hologram to auto-generate a live style guide from these comments, which provides a browsable inventory of components, their variants, instructions on how to use them and what they should look like.
<a name="doc"></a>
/*doc
---
title: Layout Grids
name: grid
<a name="category-layout"></a>
category: Layout
---
_The body text is ideal for more detailed explanations and
documentation. It should include a functional example of HTML for the classes in question._
_Be sure to indicate what classes and/or elements are extended by this class:_
Extends `ur-grid`
Extends `<a href>`
TODO: This is a todo statement that describes an atomic task to be completed
at a later date. It wraps after 80 characters and following lines are
indented by 2 spaces.
*/
In the doc header (the "front matter" between triple hyphens):
- title and category values should be capitalized.
- name values should be lowercase, with hyphens as word separators. Component docs are sorted by name, so plan accordingly.
- Don't include class prefixes in titles, names, or categories.
- Try to keep category names to a single word.
You can organize docs hierarchically by specifying a parent instead of a category. Such "child" elements will be listed in alphabetic order below the parent and inherit the parent's category. The parent value should match the name of another component.
<a name="doc-1"></a>
/*doc
---
title: Inner Grid
name: grid-inner
<a name="parent-grid"></a>
parent: grid
---
*/