Modern front-end development

This post summarises what I would consider to be a base set of standards for modern front-end development. Over the last five years there has been a significant increase in the amount of tools, technologies and expertise in this field. I think it’s worth considering what is the minimum we should be doing to help us create good user interfaces that are usable, highly performant and easy to maintain and iterate on.

I’ve divided the post into five sections:

  • Workflow
  • HTML
  • CSS
  • Javascript
  • Performance

Workflow

This section is about the broad approaches we should be taking when building websites, how we can speed up our development process and make our UIs easier to maintain.

Build websites responsively

We should create websites that adapt to any screen size. If you’re in need of convincing then Brad Frost’s This Is Responsive collection of resources does a good job of making the case for responsive web design (RWD) as well as how to actually do it!

RWD also has consequences for our workflow. As Andy Budd says in The Pastry Box:

Responsive design is an integrated process that requires designers and developers to work in harmony. It’s not a process that involves a designer mocking up 3 different versions of a page (small, medium and large) and then handing it over the wall to a developer.

Progressive enhancement

We should make sure that whatever we build works in the least-capable browsers and only then add HTML5/CSS3 features for more advanced browsers. This means checking that our UI is functional in IE 6/7/8 or Android 2.3 (a browser matrix helps here) before adding these extra goodies.

The web is an open platform and the browser is a constantly evolving beast, therefore, we should strive to make our websites as accessible as possible.

Accessibility

User interfaces should be usable for people with disabilities. At the very least this should involve meeting some basic criteria such as the checklist provided by the A11Y project.

Think of a website in terms of modules rather than pages

Websites are made up of a number of different components. For example, the page you’re currently reading has a header, a blog post, a comments section and a footer. We can break some of these down even further: the blog post has a series of headings, paragraphs and lists.

When building websites we should encapsulate these components as modules that have a single responsibility, work as a stand-alone component and can be used with other modules to make up a web page. This is a much more effective way of building websites for the following reasons:

  • Developers can work on different web page elements simultaneously without getting in each other’s way
  • Existing page elements can be changed without worrying that this might break something else
  • New page elements can be created quickly and easily
  • Page elements can inherit from other elements
  • UIs become more flexible since page elements can be easily included in different places without additional work

Test everything

We should:

Automate everything

In his post on “What makes a good engineering culture” Edmond Lau argues that organisations should “push relentlessly toward automation”. We need to automate many common and repetitive tasks preferably with Grunt and Bower. These include, but are not limited to, the following:

  • Compiling CSS preprocessors
  • Running CSS linting and Javascript linting tasks
  • Concatenating and minifying stylesheets and scripts
  • Optimising images
  • Generating documentation
  • Enforcing code formatting rules
  • Running tests
  • Downloading and updating vendor libraries
  • Generating boilerplate HTML, CSS and JS (Yeoman is good for this)

When writing this post I came across Addy Osmani’s presentation on “Automating Front-end Workflow” which discusses these things and other useful tools.

Continuous integration

If we want to test and automate everything it’s important that this is done as part of a continuous integration process. Every time a developer commits new code all of the testing and automation tasks should be run and if anything fails the team needs to be alerted and deployments prevented.

Automatic browser reloads on save of a file

With tools such as grunt-watch, LiveReload and Livestyle the old workflow of saving a file in your editor, switching to your browser and hitting ‘refresh’ should be over.

Don’t reinvent the wheel

Do not waste your clients’ or colleagues’ time coming up with your own CSS Reset (see Normalize), tabs (see jQuery UI), accordions (jQuery UI again) or DOM library (ever heard of jQuery?). There are many good solutions already out there for these kinds of problems so just use them!

HTML

Use semantic HTML

We should markup our content in a meaningful way using the latest HTML5 tags where applicable. This includes (but not limited to):

  • <section>
  • <article>
  • <header>
  • <footer>
  • <nav>
  • <figure>
  • <figcaption>
  • <video>
  • <time>
  • <progress>

Favour default HTML5 behaviours over custom ones

Forms are a good example here. We can specify required fields, placeholder text and regular expressions to test against with HTML5. The <datalist> element can help users autofill input elements.

We should use methods like these rather than implementing our own behaviours with Javascript.

Web components (in the near future)

Web components will become the default way for building websites in the not-too-distant future. It will help us build modules rather than pages (see above), provide encapsulation for our components and make the web platform more flexible. As soon as this feature has wide browser support we should use it immediately.

CSS

Architect our CSS

If we want to build modules rather than pages and make our modules easy to test we have to architect our CSS in a way that assists this process. We should decide on an approach for how we are going to write CSS that is enforced through linting, code reviews, automated checking or a combination of these methods. My preference is to follow an OOCSS approach using something like SUIT.

Even if you don’t want to use OOCSS it’s very important that conventions are laid down for writing CSS. Without this our websites will become harder to develop for and iterate on and building new features can cause regressions in other areas of the site.

Lint our CSS

To help enforce an approach to writing CSS use something like CSSLint.

CSSCSS

In order to keep code DRY and user interface components consistent using something like CSSCSS can be very helpful.

Use CSS3 (especially for animations)

We should favour using CSS3 over image-based or Javascript solutions. For example, we can use images to create rounded corners but we should use border-radius. We can write animations with Javascript but we should use CSS Animations or Transitions. In many of these cases we should avoid providing fallbacks for browsers that don’t support these features since this adds complexity to our codebase and the user experience won’t suffer without them.

CSS preprocessor

Using something like LESS, SASS or Stylus helps us keep our code DRY, encourages re-use of CSS and helps to take away some of the pain points of developing for the browser such as dealing with vendor prefixes. Even simple preprocessing such as vendor prefix generation and enabling use of CSS variables (an approach taken by Twitter) should be part of our workflow.

Flexbox (in the near future)

Flexbox solves many of the common layout issues front-end developers face such as vertical centering, equal height columns and simple grids. It’s supported in all modern browsers so we’re just waiting for a few stragglers such as IE8 to die out before it will be a staple of front-end development.

Javascript

Architect our Javascript

Similarly to CSS we should architect our Javascript so that we can keep to a modular structure, make our code DRY and enable code re-use. My preference is to use Backbone to give Javascript a basic structure. It is fairly easy to understand, well-supported and has a large community of users. Without deciding on an approach for writing Javascript we will find it harder to embrace change and lose efficiency.

Use a module loader

Continuing the module theme we should be using module loaders such as RequireJS or Browserify to handle modules and their dependencies.

Test-driven development

Any time we need to build a new feature using Javascript we should write tests for that feature first and only then write code that implements the feature and passes the tests. TDD is a very powerful method for writing clean and concise code and helps to prevent regressions and bugs.

Lint our Javascript

To help enforce an approach to writing Javascript use JSHint or JSLint. They can also assist with catching common errors.

Consider using an MVC framework for more “app-like” experiences

There is a time and a place for moving our application logic to the client rather than the server. In these cases we should consider using frameworks such as Angular, Ember or Marionette.

Performance

In order to deliver good experiences we need to make sure our websites load quickly and work smoothly. This section discusses some things we can do to improve performance.

Do the basics

All front-end developers should know that every extra http request on a page and every additional kilobyte downloaded hurts performance. We should always do the basics:

  • Concatenate and minify CSS and Javascript
  • Optimise images
  • Sprite background images
  • Use gzip
  • Cache resources
  • Put scripts at the bottom of the page
  • Serve appropriate images based on the user’s screen size

There is a lot more we can do but these are simple things that improve download time. I would recommend looking at the Google pagespeed module for Apache and Nginx that talks about additional performance improvements we can make and automates many of these things for us.

Minimise browser painting and reflow

Good UIs are smooth, pleasant to use and avoid jank. One of the most expensive operations a browser can perform is rendering new DOM elements and recalculating the position of existing elements. We can minimise these operations by, for example, adding new elements to a page in a batch rather than one at a time. When moving elements we can ensure that they are positioned in such a way that they do not affect the layout of other elements.

Use hardware-accelerated animations (especially for mobile)

With CSS3 Transforms we can use hardware-accelerated animations. This is particularly important for mobile devices where animations can feel very sluggish depending on which properties we’re animating. The only properties that benefit from hardware-acceleration are the following:

  • transform: scale
  • transform: translateX
  • transform: rotate
  • opacity:

We should aim to only use animations with these properties. Of course animating things like width and height forces recalculations of the elements around them and these are expensive for the browser.

Conclusion

In this post I’ve summarised how I feel front-end developers should be working today. I think following these practices facilitates building good user interfaces that are enjoyable to use and easy to maintain.