A web page doesn't have to look exactly the same, or be experienced exactly the same in all browsers. When designing and building websites, it's to incorporate the advantages of modern web techniques for those browsers that support them, as long as the design and build is done in such a way that the core content and functionality is available to older browsers too. u
Web standards, best practices and code conventions are the three different levels of coding goodness required to build a good quality site with respect to its client side code.
Web standards describe the flavour of code to be used to build the website. These are the set of finalised or emerging standards from bodies that are recognised as owning the standards for a particular web technology. These are the W3C, WHAT-WG and ECMA.
In today’s fractured landscape of myriad devices and browsers, it is no longer meaningful to say that a web page must look exactly the same, or be experienced exactly the same in every browser. In this context, web standards provide the best insurance that sites/apps will work as intended across the broadest range of devices and browsers, now and in the future. This is because browser manufacturers are working together within the standards system to support web standards for their current and future products.
Best practices describe some of the recognised ways code can be nuanced to provide known benefits to users. These include various coding techniques to improve the user experience. For example a performance, security, or other benefit.
Within the context of a standards-based website, most pieces of web functionality may be coded in more than one way. Sometimes it doesn’t really matter how something’s coded, but often it can. For example, one approach may cause a longer perceived download for the user than another, or be less secure.
Coding conventions relate to code idioms that provide benefits in the context of the codebase. The aim is to achieve consistency of coding style between developers, to make maintenance easier, and so more efficient. For example, by improving code readability, reducing redundancy, making it easier to run diffs, etc.
The nitty gritty of how code is written can make it easier for other developers (or yourself in the future!), to understand. Although some idioms provide more or less benefit over others, the fact of consistency in code idiom between developers makes it easier to read the codebase, as you’re already familiar with the coding style used, whoever wrote it. This makes it easier to spot bugs before things progress too far, and generally quicker to get up to speed with unfamiliar code.
All new websites should be built using a mobile-first, responsive design approach. Web sites should be developed according to the principle of progressive enhancement:
- Start with appropriate, good, content
- Mark up the content in semantic html
- Then add layout and formatting with CSS
- Finally add behaviour using JavaScript
Using this layered approach enables everyone to access the basic content and functionality of a web page, whilst providing a more enhanced version of the experience to those users with more fully featured browsers. It is a forward looking approach, and as the web is evolving so rapidly, this is the best start you can give your site to be future friendly.
Some older browsers and devices can’t quite measure up to delivering the experience expected by most users these days using standardised methods, though. However, there are work-arounds for a lot, but not all, of these. Where a work around exists, a judgement must be made as to whether its benefits are worth any trade offs involved. This decision may need involve other parties, not just technical (e.g.UX, design,editorial, etc).
In keeping with this philosophy is the technique of Responsive Design, originally articulated by Ethan Marcotte. This allows much more flexibility of layout, and therefore presentation of content, with respect to viewport dimensions than a traditional stand-alone fluid layout. It layers CSS3 media queries, and some technique to handle media contextually, on top of a fluid layout, and is potentially very powerful.
Width-based media queries can either be triggered when the viewport goes above a certain width, or when it goes below a certain width. The most small-viewport-friendly approach (called "mobile first"), uses media queries triggered on a min-width condition. The alternative, using max-width-triggered media queries, means the mobile user will have to load in all the desktop-focused things, often on a slow cell connection, and then probably not use a lot of them.
Whatever the flavour of responsive design, consider the fallback experience for users whose clients don't support media queries. Understanding your users' needs and how best to fulfil them, within the context of achieving your site's business objectives, will give you the best indication of which approach to adopt.
Note that this should force some thinking up front around the site's business objectives, some user research into audiences, and user testing to elicit how best to meet the users' requirements.
Browsers' adoption of html5 developing standards are very rapid, which makes it impossible to give a definitive statement of which parts of the technology are stable enough to use in production, versus those which are still not yet mature enough. It is essential to understand what experience you are trying to produce, and the browser/device environment in which it is to operate. There are several online tools that provide a good reference to what html5 features are supported in which browsers. caniuse is a good example of this. Use it.
As well as tracking the advances in the modern browsers, it's important to be aware of the polyfills available to old versions of IE (pre IE9). Understand what they provide, what the tradeoffs are, and how this aligns with whatever constitutes the minimum viable product for what you're building.
Recommendation: use the html5 doctype declaration for all new websites to enable forward compatibility.
Legacy sites may be updated to use this if no ill effects are found in doing so, but the older the code, the more fragile things tend to be, so tread carefully. If the old site is rendering in quirks mode, it might not be worth the pain of transition.
Recommendation: use the html5 encoding syntax:
<meta charset= "utf-8" />
when stating the encoding as UTF-8, obviously make sure your documents are actually encoded as UTF-8!
Recommendation: omit if using the html5 DOCTYPE.
Relates to: <main>, <article>, <header>, <menu>, <nav>, <section>, <footer>, <hgroup>, <aside>, <figure>, <figcaption>
- These elements are not recognised natively in IE8 and below.
- Unrecognised elements are (correctly) styled by the browser as inline elements.
- These elements can be polyfilled by html5shim and others to be made to display correctly in IE 8 and below.
- But, if the attempt to polyfill fails (for example if Javascript is unavailable), the display of these block elements will break. This will look really, really bad, and may well result in an unreadable/unusable webpage.
Recommendation: safe to use as long as you have high confidence that your users on IE 8 and below will have JavaScript enabled
If they don't, you can use the following pattern to describe the html5 markup using (semantically meaningless) divs. Assign a class to these divs that will enable easy transformation to html5 markup later, when the time is right. Suggest the following:
<div class="[name]">
...
</div>
So content that could be markup up using an html5
<div class="section">
...
</div> <!-- /.section -->
Which will enable an easy grep to update the html later to
<section class="section">
...
</section>
If you're using low-specificity selectors to target your CSS (classes), then the change of element should be transparent to your CSS.
<hgroup>
has now been deprecated by the W3C. Use these alternative markup patterns.
Relates to: <time>
, <mark>
Recommendation: safe to use. If browsers don't understand these inline semantic elements, it will display their contents in the default display mode, which is inline, and so this fallback is safe.
Recommendation: DO NOT USE. The html5 document outline allows for multiple <h1> elements on a page, one per sectioning element. This is broken pretty much everywhere. Use a heading of the appropriate rank for the section's nesting level. As it says on the Paciello Group blog from 28 October 2013: "The Document outline is a concept that lives in the HTML specification, but is essentially a fiction in the real world. It is a fiction because user agents have not implemented it and there is no indication that any will."
Recommendation: okay to use. The html5 form attributes are safe to use, as browsers that don't understand them will fallback to type# "text". Be aware of this fallback, and ensure that the form is designed/built in such a way that it makes sense, and is still easy to fill in with the fallbacks in place.
Remember that different browsers have different UI styles for their form controls, so a native range slider in Opera may not look the same as the one in Chrome.
If using html5 form validation, make sure you supply a fallback method for browsers that don't understand it.
html5 defines native elements to handle audio and video, eliminating the previous reliance on Flash, for those platforms and browsers that support them. Like many aspects of html5, the media elements come with an associated API that can allow you to do some powerful things. As always, consider fallbacks, what your minimum viable product is etc when you decide your approach.
Bear in mind the following:
- iOS devices do not play Flash content
- IE < 9 does not recognise native html5 media elements (nor API calls, obviously)
- Different audio/video codecs are supported in different browsers:
- at the time of writing, most browsers support the mp4 video codec, except desktop Firefox, and all Opera browsers (Google has stated that Chrome may stop supporting this in the future)
- The WebM codec is supported by all browsers except Safari and IE.
This means that if you're encoding your own video content, if you want to hit everybody, you'll need a WebM version, an mp4 version, and a Flash fallback version.
You could avoid the pain involved by uploading the video to YouTube and letting Google handle the encodings automatically, this seems to give an acceptable experience for most platforms tested. Note that current empirical evidence (me on my Galaxy SII), suggests that Vimeo videos don't work in Firefox on Android, probably because Vimeo don't encode in WebM, only mp4. The moral of the story here is to not be afraid of testing in order to work out the best approach for what you're trying to do.
Consider these on a case-by-case basis:
- be aware of what's supported where
- understand any accessibility implications
- understand polyfill options
- agree where failing gracefully is an option
- be particularly aware of mobile and legacy browser issues when devising an approach
- remember, it doesn't have to look the same, or be experienced the same, in every browser
Refer to caniuse etc where necessary, and don't be afraid of testing.
All websites should conform to the AA level of the WCAG2 recommendation.
Previously a seperate recommendation, WAI-ARIA is now being incorporated into the html5 spec. Although the official line from Hixie (WHAT-WG) is that an html element with both a role and aria-role attributes does not validate, in the short to medium term, the advice from accessibility specialists is to use both until the spec stabilises and the accessibility user agents catch up. See also this developers guide.
ARIA is an extensive (sub)specification, but the two most important aspects are:
- always use landmark roles, where appropriate (unless the above links specifically advise against a particular use case).
- when using any kind of asynchronous content, ensure you use the correct roles to tell the screen reader that the content is changeable, and make sure you fire the correct events to indicate updates when they occur.
Note that almost all users have JavaScript enabled in their screen readers these days. The source for this and more screen reader stats is the WebAIM screenreader survey
The CSS3 specification is split into a number of modules, each with their own track in the W3C process. This means that delay in one aspect of the spec will not cause everything to be held back. Because of this, some rapidly iterated modules are already into a level 4 (CSS4) spec development (for example Selectors level 4). However, the term "CSS3" is generally understood to mean something being defined in a level 3 spec or higher, and that's the definition used here.
As with html5, be aware of what you're trying to do, and what your user base is. Use progressive enhancement, and don't be afraid to offer users of modern browsers a better experience as long as the core proposition remains accessible and functional to all.
Media queries deserve special mention. They are available in modern browsers, only IE8 and below don't support them by default. Javascript polyfills will allow media query-like behaviour in these legacy browsers, for example css3-mediaqueries-js, so this is an option.
ECMAScript5 (ES5) is supported in all modern browsers. Legacy-wise, IE9 has only a limited implementation, and IE8 and below have none of it (they have the older ECMAScript3 (ES3)). Use the ES5 features where you can, and use published polyfills (or write your own if it makes more sense to), to plug the gaps if necessary. For example to emulate the ES5 forEach array method in older browsers, use the approach described on MDN.
When branching code, or otherwise deciding when to emulate ES5 features, use duck-typing. Never use browser sniffing on the client.
"use strict"; in new code. Apply at the function level, not at the globlal level. This will be ignored in browsers that don't support ES5, but will ensure you write good code for those that do. Understand the conditions it imposes on your code. Often this is mistakes becoming errors. For example, undeclared variables are not automatically pushed into the global namespace in strict mode, referencing an undeclared variable is an error. Another example: trying to delete a non-deletable property from an object fails silently in ES3, but in ES5's strict mode it throws a TypeError).
Caution must be taken when considering forcing strict mode in legacy code, and do not force it on code you don't control. This post recommends that you don't try to retrofit strict mode onto legacy code unless you're very sure of your testing regime.
ES6 is coming, and there are libraries that will translate ES6 into ES5 equivalent, but there is no reason to use these in production yet. The likely tipping point will be when only supported legacy browsers do not understand ES6. Track ES6, but avoid for now.
Ensure that html elements with both name and id attributes have the same value for both. There is a bug in the getElementById() implementation of some older versions of IE that results in the wrong element being returned if name and id have different values (it's effectively getElementByName...).
- Minimise the number of http requests a page makes. Concatenate CSS and JavaScript files on production servers where possible.
- Keep footprint low by optimising images, and minifying js and css where possible.
-
By default put <script> elements that contain synchronous JavaScript at the bottom of the page, just before the </body> so they don't block page loading/rendering. Sometimes JavaScript needs to go in the <head>, for example the call to the Modernizr library, and the html5shim polyfill, and this is fine, but if there's no special requirement, they should go at the bottom of the </body> element.
-
Load JavaScript asynchronously where you can. You can use the defer attribute, or one of Nicholas Zaka's asynchronous loading techniques (see High Performance Javascript, or yepnope.js either standalone or as part of Modernizr
-
Use event delegation rather than registering many event handlers
-
Always code event handlers for the bubbling phase, not the capturing phase, as older version of IE don't support capturing phase events.
-
If you can, use CORS. Only use JSON-P if you trust the domain sending the data.
- Try to avoid using the universal selector (*), as this is very slow.
- Don't use IE's proprietary filters, no matter how tempting. They are not standard and can be very resource hungry.
- Avoid inline CSS and JavaScript. It can cause over-specificity in CSS, and is generally less maintainable.
- Use the lowest sensible selector specificity to avoid specificity wars.
- Avoid using id selectors.
- If you find yourself reactively using !important (i.e. to make your style take, rather than because form validation message may require it, for example), then you've lost.
- Use one line for each property declaration (makes diffing easier).
- There is now an extensive style guide over here
Use The Good Parts of JavaScript, and avoid the bad parts, as defined by Douglas Crockford. This was conceived in the context of ECMA3, but whether you're using ECMA3 or ECMA5, you should still avoid the bad parts. Briefly, the main points are:
- Minimise globals. Use self-invoking functions to run code on load outside of the global namespace, or use one global-level namespace object and make everything a propery of this (either directly, or as a descendant)
- Never, ever use eval. Anything could be run, and it pollutes the global scope.
- Always terminate lines with semicolons. JavaScript's automatic semi-colon insertion is not to be trusted, and not using them will break attempts to minify your js
- Always use strict (in)equality operators, (!== and === ) unless you want the type coercion provided by !== and === . And if you think you do, are you sure?...
- Always supply the radix parameter to parseInt() calls.
- With can be ambiguous. Avoid.
- Always wrap blocks in braces, even where technically optional.
- If you must use a switch statement, always break at the end of each clause, never fall through (very hard to debug)
- Always provide a default block at the end of a switch statement, even if it's empty.
- Prefer function expressions over function statements to avoid issues around function hoisting
- Declare variables at the start of each function. Avoids confusion over variable hoisting (remember JavaScript scoping rules).
- Use a single var statement for each variable, comma separated, one per line.
- Always begin the name of a constructor function with a capital letter. Makes it clear if "new" keyword accidentally omitted when invoking.
- When writing constructor functions, write trapping code to ensure that if accidentally invoked without new, it returns correctly and doesn't trash "this".
- Create a new array with [], not new Array()
- Create a new object with {}, not new Object()
- There is never any need to call new String(), new Number(), or new Boolean(); so don't.
Because IE 8 and below don't support any of the modern web standards, many people have written javascript libraries to help smooth over some of the wrinkles and give a decent enough emulation of the new shiny to those older versions of IE. Actually some are used for IE9 as well: although many orders of magnitude better than its predecessor, IE's long release cycle means that IE9 still lacks a number of the features other contemporary browsers implemented long ago. Now, with IE10, there's finally a very respectable contemporary browser from Microsoft. Although if Microsoft don't considerably shorten their release cycle, it is likely to cause IE10 to age relatively quickly, as other browsers quickly forge ahead again.
A common-interface library aims to provide a common, custom interface to code against, to perform common functions across browsers (think facade pattern). These are useful when browsers offer different native interfaces to do the same job. For example IE8 and below use non-standard event binding syntax, which makes writing native cross-browser event code inefficient. To reduce the overhead of coding effort, the jQuery library provides a common event-binding syntax that handles the different browser models behind the scenes, so the developer doesn't have to worry about them.
Examples of some common interface libraries for javascript events, DOM manipulation and more, include:
- jQuery (consider Zepto when building a new site, as smaller than jQuery, and almost the same - but no animations. But note that Zepto doesn't support IE <=8).
- prototype/Script.aculo.us
- MooTools
- YUI3
An example of graphical common interface library is:
- raphael.js - this provides a common interface for vector graphics coding: uses SVG or VML behind the scenes, depending on the browser
A detection library tests the capability of the browser and provides code hooks (commonly CSS or Javascript, or both), that can be used to branch behaviour depending on what the browser supports. A common detection library is Modernizr. It's very popular, and is well maintained and supported. Its use is encouraged.
A polyfill helper library aims to emulate modern web functionality in (typically) IE8 and below. There are myriad available, for many of the aspects of html5 and css3 that are worthwhile to emulate in older browsers. The difference between polyfills and common interface libraries is that polyfills don't ususally require any special syntax. Once loaded they do their work silently from a developer's point of view Some examples include:
- html5shim/html5shiv - Remy Sharp's library to allow use of block-level html5 semantic elements in IE8 and below.
- ie9.js - "a JavaScript library to make MSIE behave like a standards-compliant browser." by Dean Edwards.
- JSON2 - Doug Crockford's emulation of JSON for IE8 and below. [Note that this implementation uses eval() (really! From Crockford himself! ;-), so make extra sure the data you pass in is clean. Actually, there's good checks in there to make sure that what's eval'd won't be evil, but it's worth knowing] ... and many, many more...
-
Bear in mind that by definition all of these libraries require Javascript in order to work. Consider your user base, and the principle of progressive enhancement when deciding on your approach. Evaluate depending on your specific requirement.
-
Note that libraries can't completely make up for missing functionality. For example IE8 and below don't support event capturing, and this is not something that jQuery can emulate. The moral of the story here is that although helper libraries are incredibly useful, they can't do everything, so understand what they can't do, as well as what they can.
-
Some libraries may create more problems than they solve. For example, there's a particular library that emulates the CSS3 box-shadow property in IE8 and below, that creates so much spurious markup, and http requests for images, that it's very likely to cause a performance hit for the browser. You have to ask yourself: does the implementation approach of the library cost the user more than the promised benefit? If the answer is a likely yes, then steer clear.
- W3C - html, css, etc. Basically all web standards except JavaScript
- WHAT-WG - html5 from the browser vendor's perspective (but not Microsoft)
- ECMA - home of the JavaScript (formally: ECMAScript) standard
- Understanding Progressive Enhancement - Aaron Gustafson on A List Apart
- Graceful degredation versus progressive enhancement - by the W3C
- Progressive enhancement on Wikipedia
- caniuse - shows which features are supported in which browsers. Use it.
- html5 please - another feature support research tool
-
jQuery
-
prototype/Script.aculo.us
-
MooTools
-
YUI3
-
raphaeljs
-
modernizr
Distilled from my conversations with Josh Emerson.
Update: Unlike Clearleft, we now use 2 spaces for indentation
For users:
- performance
- device agnostic
- accessibility
- progressive enhancement
For creators:
- right tools for the job
- minimum possible complexity
- standards
- polyfill as last resort
For both:
- loose coupling
- modularity
- maintainability
- readablity
HTML:
- indent with 4 spaces (not tabs)
- indent block level elements
- comment closing page elements
CSS:
- indent with 4 spaces (not tabs)
- new line for new selector
- set styles on classes, not elements nor ids (i.e. always aim for as low a selector value as possible)
JavaScript:
- indent with 4 spaces (not tabs)
- same line curly braces, for example:
function () {
...
}
- camelCase variable and regular functions names
- PascalCase constructor functions
General:
- feature detection, not browser sniffing
- lazy-load additional (non-core) content/functionality
- non-blocking preferred
- minimize requests
- gzip and cache
- compress assets
It's easy to get confused by the imprecision found in the term "html5" these days. To (hopefully) clarify a bit:
- "HTML" refers to the markup language. If no version is specified, it refers to the markup language defined in the W3C draft html5 recommendations, and/or the WHAT-WG html living standard.
- As well as defining a markup language, the core draft specifications/recommendations of html5 include a number of Javascript APIs, so "html5 specification/recommendation" may refer to both HTML markup and Javascript APIs.
- To complicate matters even further, there are a number of Javascript APIs that were originally in the core html5 definition, that have now been put into their own specification/recommendation tracks, by the W3C and/or the WHAT-WG. So when you see the term "html5", this could actually be referring to one of these.
Essentially "html5" means a-specification-or-recommendation-of-modern-web-related-html-and-javascript-apis-looked-after-by-the-W3C-and-or-WHAT-WG.
And if, by this point, you're thinking that the language surrounding html5 is as messy as the browser landscape within which it sits, you might just have a point...