This document details the guidelines and standards adhered to by the Creative Technology department at TMW, and all web applications built should take these into consideration. It is an evolving document and should be reviewed as and when required to keep up with changes in technology and best practice.
These guidelines have been compiled looking at various previously written guidelines - credit goes to Isobar and CSS Wizardry both of which have been used as foundations to build upon for this document, and in some sections been directly quoted.
For all languages, indent your code with tabs. The default tab size should be set as 4.
We encourage readability over file-size when it comes to maintaining existing files. Plenty of white-space is encouraged, along with ASCII art, where appropriate. There is no need for any developer to purposefully compress HTML or CSS, nor obfuscate JavaScript.
We will use server-side or build processes to automatically minify and gzip all static client-side files, such as CSS and JavaScript.
We test in the latest 3 versions of Firefox, Chrome and Safari due to them using incremental auto-updates.
We use Kickoff, a lightweight front-end framework we maintain for creating scalable, responsive sites.
When building static templates, we use Statix, which integrates Kickoff with Assemble to make templating faster and more maintainable.
Both Kickoff and Statix are actively maintained by the Creative Tech team at TMW; for more information about their features, getting started and demos, see the Kickoff documentation site.
The HTML5 Doctype and HTML5 features will be used on projects when appropriate.
To ensure HTML5 markup compatibility with older browsers, use either:
We will test our markup against the W3C validator, to ensure that the markup is well formed. 100% valid code is not a goal, but validation certainly helps to write more maintainable sites as well as debugging code. TMW does not guarantee markup is 100% valid, but instead assures the cross-browser experience is consistent.
The following are general guidelines for structuring your HTML markup. Authors are reminded to always use markup which represents the semantics of the content in the document being created.
<p>
elements for paragraph delimiters as opposed to multiple <br />
tags.<ul>
, <ol>
, or <dl>
, never a set of <div>
s or <p>
s.Place an HTML comment around DIV tags that contain a larger amount of markup to indicate the element you're closing. It will help when there is a lot of nesting and indentation. For example:
<!-- Start of .contentWrap -->
<div class="contentWrap">
//some markup goes here
</div> <!-- End of .contentWrap -->
Make use of <thead>
, <tbody>
, and <th>
tags (and Scope
attribute) when appropriate.
<dl>
(definition lists) and <blockquote>
, when appropriate.<label>
fields to label each form field. The for
attribute should associate itself with the input field, so users can click the labels and obtain focus.size
attribute on your input fields. The size
attribute is relative to the font-size of the text inside the input. Instead use CSS width.text-transform: uppercase/lowercase
.hCard
and adr
....and the single most important rule...
While the HTML5 specification defines quotes around attributes as optional for consistency with attributes that accept whitespace, all attributes should be quoted.
<a href="mylink.html" title="My Link Title" data-attribute="32">This is my Link</a>
All markup should be delivered as UTF-8, as it's the most friendly for internationalization. It should be designated in both the HTTP header and the head of the document.
Set the character set using <meta>
tags:
<meta charset="utf-8">
Consider ARIA integration for high accessibility sites.
For our full guidelines on Accessibility, refer to the Accessibility Guidelines section of this document.
<head>
of the document.<link>
tag to include, never @import
.:before
and :after
CSS pseudo-elements if styles are not 100% necessary.Selectors should be specified using a simplified version of BEM:
/* Descriptors use camel-casing if more than one word: e.g. twoWords */
.skipToContent {
...
}
/* ========= */
/* Child elements use single hyphens: - */
.form-controlGroup {
...
}
/* ========= */
/* Modifier element use a double hyphen: -- */
.btn.btn--primary {
...
}
/* ========= */
/* 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.
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"] {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
padding: 10px 0 0 0;
margin: 10px 0;
background: red;
border-radius: 10px;
-moz-border-radius: 10px;
}
For each level of markup nesting, indent your CSS to match. For example:
nav {}
nav li {}
nav li a {}
.content {}
.content p {}
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: - The Nav Abstraction
@font-face should be used for font replacement where possible - ensuring that the font can be safely used in .ttf format on the web in agreement with its licensing agreement. Where this is an issue, look to use tools such as TypeKit or Fontdeck
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.
There is no set preference to using a reset CSS file or using a normalisation technique, as long as consistency is applied throughout projects.
If a reset is preferred, the Eric Meyer reloaded reset should be used.
For normalisation, the excellent normalise.css should be included.
Kickoff, which is used as a base for all of TMW‘s projects, chooses to implement normalisation by default, rather than a CSS reset.
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 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/margin's 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 */
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 effect 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.
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
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 7]> 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.
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.
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.
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 your images to data URI's and including them in the CSS avoids HTTP requests entirely. This is however at the expense of older browser support, namely Internet Explorer 7 and earlier.
At TMW, we currently use a combination of GruntIcon, and data-uris, but this is reviewed on a project-by-project basis.
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.
Use of a preprocessor should be used on a per project basis where it is deemed necessary.
Where a preprocessor is used, we shall use Sass. Kickoff, the TMW 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.
All work is considered in a responsive, mobile first, way unless the project dictates otherwise.
Responsive design isn't just a way of developing, it is a way of thinking that needs to flow through the entire site development process from content, UX, design and development.
It is recommended to read Ethan Marcotte's excellent book, Responsive Web Design as well as the related A List Apart article on the subject.
It is out of the scope of this document to go through the specifics of developing responsively, but some suggested techniques that we use during development are talked about in Sass – Mobile First.
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 {}
}
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.
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. This solution is also built into Kickoff, TMW's front-end framework.
<head>
.$
) character e.g $headerChildren
Build using the object literal pattern e.g.
var SiteSetup = {
init: function () {
this.$sections = $('#container section');
this.$additionalTextNodes = $('section a > span');
this.createMarkup();
},
createMarkup: function(){
var $additionalTextNodes = this.$sections.remove();
&additionalTextNodes.css({
position: 'absolute',
top: this.style.left - $sections[0].style.width / 2
});
}
}
SiteSetup.init();
Structure and formatting should follow the example below:
$(function(){
var SiteSetup = {
ANIMATION_SPEED: 100,
init : function () {
//fire off all other classes
if (LightBox.$lightbox.length > 0) {
LightBox.init();
}
}
},
LightBox = {
$lightbox: $('.lightbox'),
init : function () {},
open : function () {},
close : function () {}
};
SiteSetup.init();
});
Documentation should follow NaturalDocs structure. As with all code, document as frequently as you can - the more detail the better. At the very least, document each function you create.
Read: The Essentials of Writing High Quality JavaScript
Separate operators/comparators with spacing
// Good
if (foo && foo.bar && typeof foo.bar === 'object') {
foo.bar.call();
}
// Terrible
if(foo&&foo.bar&&typeof foo.bar==='object'){
foo.bar.call();
}
Use braces for logic evaluations. If evaluation execution is simple, keep non-braced logic on a single line e.g:
// Good
if (i < 10) return true;
// Good
if(foo && foo.bar && foo.bar > 10) {
foo.baz = foo.bar - 100 * 2.7 + 'rad'
}
// Bad
if(foo && foo.bar && foo.bar > 10)
foo.baz = foo.bar - 100 * 2.7 + 'rad'
// Bad
if(i < 10)
return true;
// Bad
if(i < 10)
{
return true;
}
Remap this to self when passing context
===
as a comparator (unless you really need flexible evaluations e.g comparison to null)true
/false
$.extend
if you are using jQuery to extend a passed in object while providing defaultsIf possible, avoid using bitwise operations unless they really help. If used, document them with comments
// inverting bits to ease comparison to -1
if (~foo.bar.indexOf('leetness')){
alert('w00t!')
}
When considering how we use JavaScript on a project, no two projects are usually the same in how it uses libraries, although the style and inclusion of the libraries is kept as consistent as possible.
Many of our projects are developed using a mix of native JavaScript and jQuery, although the use of jQuery or simply a native solution is considered at the start of each project.
Kickoff, the TMW front-end framework, includes jQuery as part of it's default build, but this can be easily removed, especially if using the Yeoman generator for Kickoff, which will ask if you would like to include it as part of your project setup.
We maintain a number of JavaScript classes and plugins we have built and extended across a number of projects. These can be found on our github repo, the readme of which details our actively maintained projects.
We also maintain an active wiki detailing a number of JavaScript and jQuery plugins we use for common use cases such as form validation, carousels and lightboxes. Any additions to this must first be added to the experimental list and then approved by at least 2 Members of the team.
Dependency management and build processing in JavaScript is an area that has progressed rapidly over the last 12-18 months. Our preferred method of handling dependencies in JavaScript is to use Browserify, which uses a CommonJS syntax to manage your dependencies.
Although it is encouraged to use browserify on future projects, it is down to the lead developer on each project to choose the solution that best fits their needs.
for
or reverse while
loop (especially for large objects)on()
and off()
handlers for events. Everything else is now deprecated (live, delegate, bind)$selected.attr('data-foo')
unless working with complex data types (where you can use $selected.data()
)Learn how to use your browser tools properly as it will save you hours in debugging.
If you're using alert()
you're doing it wrong. Use console.log()
, or Paul Irish's lightweight wrapper
This list will eventually go on a separate wiki detailing more specific plugin information.
Markup and code should be written in a way that is inherently accessibile, irrespective of the project being worked on.
When marking up content, ARIA roles are the preferred method of designating the role of sections in our markup when its use may be ambiguous.
For accessibility testing, consider using one of the following: - Wave Accessibility tester
Performance should be treated as a constraint on your project. It is no use making the most beautiful site on the web if noone can be bothered to wait for it to download.
It isn’t just down to the size of your files, but the number of them. Each additional request on mobile increases the latency of your website.
CSS and JavaScript should be concatenated and minified on production servers to minimse their footprint on your website.
Images are a complex subject, especially in the realm of Responsive Design, and is one that is constantly changing. We recommend checking out PictureFill for handling responsive images, as well as reading Eric Portis' excellent article on the subject.
For image optimisation, we use:
We recommend running your web pages through one of the following tools to get feedback on the loading and optimsation of your site: