NML

CSS and SASS guidelines

CSS

General CSS Principles

  • Inline styles in markup should only be considered when inlining critical styles for performance. See Inlining critical CSS for first-time visits .
  • Add CSS through external files, minimizing the number of files, if possible. CSS should always be included in the <head> of the document.
  • Use the <link> tag to include, never @import.
  • Ensure markup and style stays separate (some style classes are allowed, e.g hidden etc). Only use style on markup if you absolutely have to (e.g extra wrapping elements); consider ::before and ::after CSS pseudo-elements if styles are not 100% necessary.

Syntax and formatting

  • Use multi-line CSS declarations. This helps with version control (diff-ing single line CSS can be a nightmare). CSS declarations should be alphabetized.
  • All CSS rules should have a space after the selector colon and a trailing semi-colon.
  • Selectors should be specified using a simplified version of BEM:

/* Child elements use single hyphens: - */
.form-control {
...
}

/* Modifier elements add an extra state */
.form-control-group {
...
}

/* Element state: .is- */
.is-active {
...
}

/* Sass variables are dash-case */
a {
color: $color-primary;
}
  • Use shorthand when specifying multiple values. Remember longhand can be shorter for single values.
  • Multi-attribute selectors should go on separate lines.
  • Don't over qualify class or ID selectors. Leads to specificity issues further down the line.
/*Bad*/
div.content {}

/*Good*/
.content {}
  • 0 requires no units
/* Good */
.bar,
.foo[href="bar"]
{
background: red;
border-radius: 10px;
bottom: 0;
left: 0;
margin: 10px 0;
padding: 10px 0 0;
position: absolute;
right: 0;
top: 0;
}

Indenting

For each level of markup nesting, indent your CSS to match. For example:

nav {}
nav li {}

.content {}
.content p {}

OOCSS

When building components, or modules, try and keep a DRY, OO frame of mind.

Instead of building dozens of unique components, try and spot repeated design patterns and abstract them; build these skeletons as base 'objects' and then peg classes onto these to extend their styling for more unique circumstances.

If you have to build a new component split it into structure and skin; build the structure of the component using very generic classes so that we can reuse that construct and then use more specific classes to skin it up and add design treatments.

Read:

Typography

@font-face should be used for font replacement where possible - ensuring that the font can be safely used in .woff format on the web in agreement with its licensing agreement. Where this is an issue, look to use tools such as TypeKit or Fontdeck

Always specify a .woff2 as well as a .woff font to take advantage of the greater compression the former offers. .ttf is only required for IE8.

(Note: we are currently devising better font delivery methods using localStorage. To be documented.)

To generate @font-face files, the Font Squirrel font-face generator should be used.

JavaScript replacement techniques should be avoided where possible, as they are painful, time-consuming and usually inaccurate. Flash replacement techniques (such as Sifr) should never be used.

Always define supporting font-size classes, in conjunction with headers to avoid restyling header sizes.

Reset vs Normalisation

A normalisation technique is preffered over a reset CSS file, as reset files can lead to a lot of extra rules. For the most part browser defaults are perfectly fine.

For normalisation, our own stripped-down version of the excellent normalise.css should be included.

Comments

Comment as much as you can as often as you can. Where it might be useful, include a commented out piece of markup which can help put the current CSS into context.

CSS will be minified and comments stripped before it hits live servers, so don't worry about excessive commenting bloating code - the benefits far outweigh any file-size worries.

This is especially true for responsive layouts where percentage width/margins have been worked out. Always comment in the ratio so that the resulting % values mean something to the next developer viewing your CSS. A random 6dp percentage will mean nothing to anyone else looking at your code.

e.g.

width: 34.042553% /* 320 / 940 */

Specificity, IDs and classes

CSS is designed to cascade, so make sure you understand cascading and selector specificity. It will enable you to write very terse and effective code.

Use of IDs and classes affects specificity massively. Only use IDs where deemed necessary, especially on larger builds. Classes are much more modular and portable. If you want to use an ID solely as a JavaScript hook, consider using the ID alongside a class for CSS styling.

Remember that an ID is 255 times more specific than one class.

Name classes and IDs by the nature of what it is rather than what it looks like. A class of blueBox-left may seem relevant at the time, but should its colour or position change, it will become meaningless. Naming in conjunction with a more OOCSS approach should eliminate this ambiguity.

Read: Shoot to kill – CSS Selector Intent

Conditional Stylesheets

Stylesheets specific for Internet Explorer can, by and large, be totally avoided. The only time an IE stylesheet may be required is to circumvent blatant lack of support (e.g. media queries, PNG fixes).

As a general rule, all layout and box-model rules can and will work without an IE stylesheet if you refactor and rework your CSS. This means we never want to see <!--[if IE 8]> element{ margin-left:-9px; } < ![endif]--> or other such CSS that is clearly using arbitrary styling to just 'make stuff work'; it will hinder the maintainability of our CSS.

When building mobile first responsive websites, it is necessary to generate a separate stylesheet for old versions of IE to ensure they aren't left with the mobile version of your website because they cannot read media queries. This can be done using SASS and is covered in the SASS – IE Stylesheet section of this documentation.

If IE specific styling is required, look to utilise Paul Irish's body/html class conditional for IE* targeting.

!important

It is okay to use !important on helper classes only. To add !important pre-emptively is fine, e.g. .error { color:red!important }, as you know you will always want this rule to take precedence.

Using !important reactively, e.g. to get yourself out of nasty specificity situations, is not advised. Rework your CSS and try to combat these issues by refactoring your selectors. Keeping selectors short and avoiding IDs will help you out here massively.

Magic numbers and absolutes

A magic number is a number which is used because ‘it just works’. These are bad because they rarely work for any real reason and are not usually very futureproof or flexible/forgiving. They tend to fix symptoms and not problems.

For example, using .dropdown-nav li:hover ul { top: 37px; } to move a dropdown to the bottom of the nav on hover is bad, as 37px is a magic number. 37px only works here because in this particular scenario the .dropdown-nav happens to be 37px tall.

Instead you should use .dropdown-nav li:hover ul { top: 100%; } which means no matter how tall the .dropdown-nav gets, the dropdown will always sit 100% from the top.

Every time you hard code a number think twice; if you can avoid it by using keywords or ‘aliases’ (i.e. top: 100% to mean ‘all the way from the top’) or—even better—no measurements at all then you probably should.

Every hard-coded measurement you set is a commitment you might not necessarily want to keep.

Images

Image names should use dashes and be named so that their use is clear i.e. icon-facebook-blue.png

It is hard to advise on a one size fits all solution for images currently. Instead there are a number of methods that should be considered and chosen from when approaching images in CSS.

CSS sprites can be very useful for combining the number of images on your site into a single HTTP request. Sprites work in every browser, although care should be taken when including large sprites on mobile devices as memory limits can be reached when large image files are used. Sprite Cow is a great tool for generating the CSS required for positioning, as is SpriteMe for generating a sprite out of the images used on your site.

Alternatively, converting images to data URI's and including them in the CSS avoids HTTP requests entirely

(Note: include more info here re grunt image solutions and svg)

Debugging

If you run into a CSS problem, take code away before you start adding more in a bid to fix it. The problem will exist in the CSS that is already written, more CSS isn't necessarily the right answer!

It can be tempting to put overflow:hidden; on something to hide the effects of a layout quirk, but overflow was probably never the problem; fix the problem, not its symptoms.

Preprocessors

Our preprocessor of choice is Sass. The NML base framework, contains a set of Sass base files that should be used.

Be sure to know the ins-and-outs of excellent vanilla CSS and where a preprocessor can aid that, not hinder or undo it. Learn the downsides of preprocessors inside-out and then fuse the best aspects of the two with the bad bits of neither.

For more specific guidelines on using Sass, read the Sass section of these guidelines.

Tools

  • Grunt - Info about Grunt build process to go here

Sass

General Sass Principles

  • Be sure to know the ins-and-outs of excellent vanilla CSS and where a preprocessor can aid that, not hinder or undo it. Learn the downsides of preprocessors inside-out and then fuse the best aspects of the two with the bad bits of neither.
  • One of the most powerful features of using a preprocessor is simply being able to separate your CSS into a number of different files that are pulled in and combined at compile time. This makes it easier to make your code more maintainable and better structured than having all of your code in one file.

Nesting

When nesting selectors, try not to nest more than 3 levels deep. If you find yourself writing deeply nested selectors, it is usually a sign that you should rethink how you have structured your markup or class declarations.

Nest only when it would actually be necessary in vanilla CSS, e.g.

.header {}
.header .site-nav {}
.header .site-nav li {}
.header .site-nav li a {}

Would be wholly unnecessary in normal CSS, so the following would be bad Sass:

.header {
.site-nav {
li {
a {}
}
}
}

If you were to write this in Sass, it would look more like:

.header {}
.site-nav {
li {}
a {}
}

@extend

Use caution when using the @extend operator. It can give unexpected results in the compiled CSS when used too liberally and can usually be avoided by using classes to extend styling in a more modular fashion.

For example, rather than writing the following:

.section-centered {
display: block;
margin: 0 auto;
}

.masthead {
@extends .section-centered;
}

Simply add the class to the masthead markup instead:

<div class="masthead section-centered">

</div>

Doing this will help to keep your styling modular and more reusable.

There are of course times that using @extend can be very useful, but don't use it as a defacto when sensible use of vanilla CSS would be just as simple.

Mobile first

In the majority of cases, sites should be built mobile first, specifiying the mobile styles as your base CSS and adding media queries to progressively enhance the experience for larger width devices and screens.

The problem with doing this is that Internet Explorer version 8 and earlier doesn't support media queries and so ignore them and render just the mobile styles. There is a way of solving this problem using JavaScript, but our preferred solution is to solve the problem using Sass.

Jake Archibald wrote about this solution which uses Sass to generate two stylesheets – a fixed width stylesheet, which is conditionally loaded for IE8 and below, and a separate stylesheet for all other browsers.