Collective #500

CSS Nesting Module The draft of a new CSS module that introduces the ability to nest one style rule inside another. Read it This content is sponsored via Thought Leaders Seeking a common language for design & engineering How design and engineering work together Read more

Web Standards: The What, The Why, And The How

dreamt up by webguru in Uncategorized | Comments Off on Web Standards: The What, The Why, And The How

Web Standards: The What, The Why, And The How

Web Standards: The What, The Why, And The How

Amy Dickens



The World Wide Web is an interesting place.

As the Internet has grown and become more common place, it has become a gigantic instrument of change in terms of the way in which we interact with the world and each other.

Like many people, my intro to web development at school was kind of bleak. Our school ICT (Information Computing Technology) lessons taught us very little, using Dreamweaver (back when it was a Macromedia product) as a platform to visually edit a personal website with the biggest lesson being “what is a hyperlink”. We didn’t even view the HTML source of our own websites!

So my education around HTML and CSS came largely from messing around with the “view source” option in websites. I learned through copy-pasting bits and pieces together to create my own websites and downloading templates for bootstrap, before I knew what bootstrap actually was.

Why Am I Telling You This?

Having recently surveyed my Twitter followers (it’s an exact science 😜), I discovered that a large chunk of people (43% of the people who voted), knew little to nothing about Web Standards and only 5% of those who voted were active contributors.

https://platform.twitter.com/widgets.js

When you look at the ways in which people learn to do web development, it is totally understandable that this might be the case. The volume of online tutorials, boot-camps and online resources for learning how to build websites has lead to an increasing amount of self-taught web developers (like me) building stuff for the web.

This is one of the great successes of the Internet; anyone can learn almost anything  —  and there being more and more resources for learning outside of academia is really positive in terms of lowering barriers to access web development as a career.

Even with free resources online there are still a number of barriers in learning how to be a web developer. I’m not saying these don’t exist — they really do — and we should be doing more as a community to tackle these.

But with the diversification of learning processes comes several challenges, including information overwhelm and knowledge gaps.

When learning how to build web-flavored things, it is very easy to get wrapped up in “how do I build the thing?” This can result in not equally considering the “why should I build it this way?” or “what are all the options for building the thing?

Consequently, it is equally as easy to become overwhelmed with the many number of ways to solve your web related problem. This can result in picking the first solution from the results of an internet search, without considering whether it is the better (in terms of most robust, accessible and secure) of the options available. 

Web Standards and the documentation to support Web Standards, provide a lot of insight about ‘the why’ and ‘the what’ of the world wide web. They are a fantastic resource for any web developer and help you to build things for the web that are functional, accessible and cross-compatible.

This post is designed to help anyone with an interest in the web who wants to get to know more about web standards. We will cover:

  • An introduction to web standards (what are they, why do they exist and who makes them);
  • How to navigate and make use of standards in your work;
  • Ways you can get involved in contributing to new and existing standards.

Let’s begin our introduction to web standards by asking, “Why do we need standards for the web?

The World Wide Web Before Standards

We can think of the world wide web as an information ecosystem. People create content that is fed into the web. This content is then passed through a browser to allow people to access that information. 


An illustration the world wide web as an information ecosystem
(Large preview)

Before Web Standards, there weren’t many fixed rules for any part of this system; no formal rules as to how the content should be created, nor any requirements in terms of how a browser should serve up that information to the people that are requesting it.

So, in a way, the web operated a bit like that children’s toy where you have to sort the different shaped blocks into the correct holes. In this analogy, the different types of browsers are the different shaped holes and the content or websites, are the brightly colored blocks.


The sorting shape toy and its differently shaped coloured blocks
The sorting shape toy and its colorful blocks (Large preview)

In the past, as a content creator you would make a website to fit the browser it would be intended for. For example, you would create an IE-shaped block to be able to pass this through the Internet Explorer hole.

This meant that this website block you had created would only fit through that one hole and you would need to rebuild your content into other shapes for it to be viewed using any of the other browsers.


Fitting an internet explorer sized block into an internet explorer sized hole
Fitting an IE-sized block into an IE-sized hole (Large preview)

Developers in the 90s would often have to make three or four versions of every website they built, so that it would be compatible with each of the browsers available at the time. And what is more, browser makers in attempts to better their competition would introduce “features” that diversified their approach from their competitors.

In the beginning, it was probably fairer to say our Internet browser to content-matching toy looked more like this:


A sorting toy with three round holes and one square hole
A sorting toy with three round holes and one square hole (Large preview)

This was because browsers were built to handle pretty much the same stuff, which was largely text-based content. So, for the most part, a website block would fit through the majority of the holes, with the exception of maybe one where it might fit — but not perfectly. 

As the browsers developed, they begin to add features (e.g. by changing their shape) and it became more and more difficult to make a block that would pass through each of the browser holes. This even meant that a block that could once fit through one particular hole, didn’t fit through that hole any longer; adding these features into the browser would often result in poor reverse compatibility.


Four versions of a hole that starts out circular but changes with each version to become more diamond like in shape.
A hole that changes over time means all blocks will not always fit through. (Large preview)

This was really damaging for some developers. It created a system in which compatibility was limited to the content creators that could afford to continuously update and refactor their websites for each of the available browsers. For everyone else, every time a new feature or version was released, there was a chance your website would no longer work with that browser.

Web standards were introduced to protect the web ecosystem, to keep it open, free and accessible to all. Putting the web in a protective bubble and disbanding with the idea of having to build websites to suit specific browsers.


Putting the web in a protective bubble and disbanding with the idea of having to build websites to suit specific browsers.
(Large preview)

When standards were introduced, browser makers were encouraged to adhere to a standardized way of doing things — resulting in cross-compatibility becoming easier for content makers and there no longer being the need to build multiple versions of the same website.

Note: There are still a number of nuances about cross-compatibility amongst browsers. Even today, over 20 years since standards were introduced, we aren’t quite at “one-size fits all” just yet.

Here’s a quick look at some of the key moments in the history of web browser development:

Year Key moments
1990 Sir Tim Berners Lee releases the WorldWideWeb, the first way in which to browse the web.
1992 MidasWWW was developed as another WWW browser, which included a source code viewer.
1992 Also in 1992 Lynx was released, which was a text-based web browser, it could not display images or any other graphic content.
1993 NCSA Mosaic was released, this is the browser that is credit for being the first to popularize web browsing as it allowed the display of image embedded within text.
1995 Microsoft released Internet Explorer, previously Cello or Mosaic browsers were used on Windows products.
1996 Opera was released publicly, it was previously a research project for a Norwegian telecoms company Telnor.
2003 Safari was released by Apple, previously Macintosh computers shipped with Netscape Navigator or Cyberdog.
2004 In the wake of Netscape Navigator’s demise, Firefox was launched as a free, open-source browser.
2008 Chrome was launched by Google and within six years grew to encompass the majority of the browser market.
2015 Microsoft released Edge, the new browser for Microsoft, replacing Internet Explorer from Windows 10 onwards.

Source: “Web Browsers: A Brief History” by Rhiannon Williams

Why We Need Standards

Knowing a bit about the history of standards and why they were introduced, we can start to see the benefits of having standards for the World Wide Web. But why is it important that we continue to contribute to Web Standards? Here are just a few reasons: 

Keeping The Web Free And Accessible To All

Without the Web Standards community, browser makers would be the ones making decisions on what should and shouldn’t be features of the world wide web. This could lead to the web becoming a monopolized commodity, where only the largest players would have a say in what the future holds.

Helping Make Source Code Simpler; Reducing Development And Maintenance Time

As more browsers appeared and browser makers began to diversify in their approach, it became more and more difficult to create content that would be served in the same way across multiple browsers. This increased the amount of work required to make a fully compatible website, including bloating the source code for a web page. As developers today we still have to do the odd include [X script] so this works on [X web browser], but without Web Standards, this would be much worse.

Making The Web A More Accessible Place

Web standards help to standardize the way in which a website can interact with assistive technologies. Meaning that browser makers and web developers can incorporate instructions into their pages which can be interpreted by assistive technologies to maintain a common (or sometimes better) end-user experience.

Allowing For Backward Compatibility And Validation

Web standards have created a foundation which allows for new websites, that comply with standards, to work with older browser versions. This idea of backward compatibility is super important for keeping the web accessible. It doesn’t guarantee older browsers will show your content exactly as you expect, but it will ensure that the structure of the web document is understood and displayed accordingly. 

Helping Maintain Better SEO (Search Engine Optimization)

Another of the major hidden benefits (at the time that Web Standards was first introduced) was that a Web Standards compliant website was more discover-able by search engines. This became more evident when the Google search became the major player in the search engine world in the early 2000s.

Creating A Pool Of Common Knowledge

A world with web standards creates a place in which a set of rules exists, rules that every developer can follow, understand and become familiar with. In theory, this means that one developer could build a website that complies with standards and another developer could pick up where the former left off without much trouble. In reality, standards provide the foundation for this; but the idea relies heavily on developers writing well-documented code. 

Who Decides On What Becomes A Web Standard?

Standards are created by people. In the web and Internet space, there is a strong culture of consensus — which means a lot of talking and a lot of discussions.

The groups through which standards are developed are sometimes referred to as “Standards Development Organisations” or SDOs. Key SDOs in the web space include the Internet Engineering Task Force (IETF), the World Wide Web Consortium (W3C), the WHATWG, and ECMA TC39. Historically there were also groups like the Web Standards Project (WaSP), that advocated for Web Standards to be adopted by organizations.

The groups that work on the Internet and Web Standards generally operate under a royalty-free regime. That means when you make use of a web standard you don’t have to pay anyone — like someone who might hold a relevant patent. Whilst the idea that you might have to pay royalties to someone to build a web browser or website might seem absurd right now, it wasn’t too long ago that organizations like BT were trying to assert ownership of the concept of the hyperlink. Standards organizations like the ones listed below help keep the web free (or free from licensing fees at least).

What Is IETF?

The IETF is the grandparent of Internet standards organizations. It’s where underlying Internet technologies like TCP/IP (Transmission Control Protocol/Internet Protocol) and DNS (Domain Name System) are standardized. Another key technology developed in IETF is something called Hyper-Text Transport Protocol (HTTP) which you may have heard of.

If you’ve been paying attention to the rise of HTTP2 and the subsequent development of (UDP-based) HTTP3, this is where that work happens. Most of the work in IETF is focused on the lower levels of the Open Systems Interconnection model.

What Is W3C?

The World Wide Web Consortium (W3C) is an international community where member organizations, a full-time staff, invited experts and the public work together to develop Web Standards. Led by Web inventor and Director Tim Berners-Lee and CEO Jeffrey Jaffe, W3C’s mission is to lead the Web to its full potential.

The community was founded in 1994 at MIT (Massachusetts Institute of Technology) in collaboration with CERN. At the time of this post, W3C has 475 member companies and organizations and exists as a consortium between 4 academic institutions: MIT (USA), ERCIM (France), KEIO University (Japan) and Beihang University (China).

Work in W3C happens in working groups and community groups. Community groups are where a lot of initial innovation happens around new web technologies. New web standards can be produced by community groups but they are officially seen as “pre-standard.” Community groups are open for anyone to participate, whether or not the organization you work for or are affiliated with is a W3C member.

W3C working groups are where new web standards are officially minted. Working groups usually start with a submission of a standard, often something that is already shipping in some browsers. However, technical work on refining these standards happens within these groups before the standard goes for final approval as a “W3C Recommendation.” By the time something reaches “recommendation” phase in W3C, it’s most often implemented and in wide use across the web. 

Working groups are more difficult for people who are not affiliated with a member organization to become a part of. However, you may become an invited expert to a group. One reason why working groups are a little more difficult to join and operate with more process is that they also act as an intellectual property holder  —  through joining a W3C working group organizations and companies agree to the royalty-free licensing laid out in W3C’s patent policy.

W3C Advisory Board member Natasha Rooney has put together a great document, W3C Process Document for Busy People, that explains a lot of the ins and outs of working in W3C.

What Is The WHATWG?

The WHATWG was originally a splinter group from the W3C. It was formed in 2007 because some browser vendors didn’t agree with the direction in which the W3C was pushing HTML. WHATWG continues to be the place where HTML is developed and evolved. However, the community of participation in the HTML specification still includes many people from the W3C community, and many WHATWG-affiliated people participate in W3C working groups. 

At the time of this post, the relationship between the W3C and the WHATWG remains in flux. From a developer perspective, this doesn’t matter too much because developers can rely on resources like MDN to reflect the “truth” of which web technologies can be used in specific browsers. However, it has led to a lack of clarity, in terms of where to participate in the development of certain standards. WHATWG also has its own royalty-free license agreement  — the WHATWG participation agreement

What Is The “Why CG”?

The Web Incubator Community Group (WICG, pronounced Why-CG) is a special community group, within W3C, where some new and emerging web technologies are discussed and developed.

If you have a great idea for a new standard, a new feature for an existing standard or a new technology you think ought to be incorporated into the web, it’s worth checking here first to see if something like it is already being discussed. If it is, great! Jump into these discussions and lend your support. If not, then suggest it! That’s what this group is for.

What Is The ECMA TC39?

Ecma is a standards organization for information and communication systems, which was founded in 1961 to standardize computer systems in Europe. Its name comes from being previously known as the “European Computer Manufacturers Association” but it is now referred to as “Ecma International  —  European association for standardizing information and communication systems” since the organization went global in 1994.

The ECMA-262 standard outlines the ECMAScript Language Specification, which is the standardized specification of the scripting language known as JavaScript. There are ten editions of ECMA-262 that have been published (the tenth edition was published in June 2018).

TC39 (Technical Committee 39) is the committee that evolves JavaScript. Like the other groups listed here, its members are companies which include most of the major browser makers. The committee has regular meetings which are attended by delegates sent from the member organizations and also by invited experts. The TC39 operates on achieving consensus, as with many of the other groups, and the agreements made often lead to obligations for its members (in terms of future features that member organizations will need to implement). The TC39 process includes accelerating proposals through a set of stages, the progression of a proposal from one stage to the next must be approved by the committee. 

What Was The Web Standards Project?

The Web Standards Project was formed in 1998 as a resistance to the feature face-off happening between browsers in the 90s; with a primary goal of getting browser makers to comply with the standards set forth by the W3C.

As the organization grew and the browser wars ended, the project began to shift focus. The group began working with browser makers on improving their standards support, consulting software makers that created tooling for website creation and educating web designers and developers on the importance of web standards. The last of these points, resulted in the creation of the InterAct web curriculum framework which is now maintained by W3C.

The Web Standards Project ceased to be active in 2013. A final blog post was created on March 1st that gives thanks to the hard work of the members and supporters of the project. In the closing remarks of this post, readers are reminded that the job of the Web Standards Project is not entirely over, and that the responsibility now lies with thousands of developers who continue to care about ensuring the web remains a free, open, inter-operable and accessible resource.

How Does Something Become A Web Standard?

So, how are standards made? The short answer is through LOTS of discussions.

Proposals for new standards usually start as a discussion within a community group (this is especially the case in W3C) or through issues raised on the relevant GitHub repository.

Across the different SDOs, there seems to then be a common theme of ascension; after the discussion has begun, it then moves up within the organization, and at each level, a deciding committee needs to reach a consensus to approve the elevation of that discussion. This is repeated until the discussion becomes a proposal, then that proposal becomes a draft and the draft goes on to become an official standard. 


After an idea has been presented, a discussion begins among a deciding committee that needs to reach a consensus to approve the elevation of that discussion. This is repeated until the discussion becomes a proposal, then a draft, and finally an official standard.
(Large preview)

Now as previously mentioned, when something isn’t an official standard, this does not necessarily mean that it is not in use within some browsers. In fact, by the time something becomes a standard, it is likely to already have widespread use across many of the available browsers. In this instance, the role of the standard is part of the normalizing and adoption process for new features; it sets out the expected use for something and then outlines how browser makers and developers can conform to this expectation. 

What Is TPAC?

Every year, W3C holds one massive event, a week-long multi-group meeting punctuated by a one-day unconference on the Wednesday (the Technical Plenary) combined with a meeting of its Advisory Committee (a group consisting of one person for every organization or company that is a W3C member). Put Technical Plenary and Advisory Committee together, and you get TPAC (often pronounced tee-pac). Although it’s a W3C-run event, you will often find people “from” WHATWG, IETF or TC39 here as well.

This past year, Samsung Internet people came together to participate in TPAC. We also sponsored diversity scholarships which are intended to bring people from under-represented groups to TPAC and to the Web Standards community.

My First TPAC

When I first heard the team talking about TPAC, I had no idea what to expect. After reading up about the event on the TPAC website, I signed myself up and booked my travel. Soon enough, I was on a train from London to Lyon with the team. 


The banner at the front entrance of the TPAC venue
The banner at the front entrance of the TPAC venue (Large preview)

On arrival, I was given my Lanyard and a map of the various rooms where all the action was happening. My goal, for the three days I was attending, was to join in with as much accessibility type things as I could. Having arrived shortly after things had begun on my first day, I stood staring at a closed door for the Accessibility Guidelines working group that I wanted to sit in on. Lots of things went through my mind at that moment; “Perhaps I should wait until the break?” “No, don’t be silly, that’s still an hour away.” “Maybe I should knock?” “But wouldn’t that be more interruptive than just going in?” “Maybe I shouldn’t go in at all…” But after a few minutes, I worked up the courage to walk into the room.
 
There was a round table set up (which is typical of a lot of these sessions) with folks sitting at the tables with laptops; along with a number of seats arranged around the edge of the room for people to join in a more observational role. Each group also had a chat room on IRC, which anyone from the W3C membership could join (whether attending TPAC in person or not). I sat at the end of one of the tables; though I’m still not sure whether that was the proper thing to do in terms of etiquette. 


The gigantic bear statue outside of the Cité Centre de Congrès de Lyon, which was the venue for TPAC 2018.
The gigantic bear statue outside of the Cité Centre de Congrès de Lyon, which was the venue for TPAC 2018. (Large preview)

Initially, I was worried that my presence stuck out as much as the gigantic bear statue outside the venue; but no-one in the room paid any mind to my arrival and so the discussion continued. The group was about to move onto receiving an update on the work being done by the Silver Task Force; a community group that is trying to make the accessibility standards themselves more accessible. 

It was really interesting to sit at the table for these discussions. Whilst as a first-time attendee, some of the language took some getting used to (terms like ‘conformance’ and ‘normative’); it was super nice to be inside of a room full of people who cared so much about accessibility. Many of the attendees of this working group spoke from a position of lived experience in terms of using the web with an accessibility requirement. Having spent my last three years researching accessibility requirements in digital music technology, I felt quite at home following along with the questions raised by the members of this group.

The work showcased by the Silver Task Force in this first discussion really sparked an interest for me. It felt like quite a refreshing viewpoint of how to make standards, in general, more accessible and frame them in such a way that makes for easier navigation and more tailored advice and guidance. For the following few days, I joined this (much smaller) group and had the chance to input into the conversations  —  which was really positive. Since TPAC, I have joined the community group for the Silver Task Force and have plans to join the weekly meetings in the new year. 


The Samsung group posing for an after-dinner picture around the table
Our Samsung group out to dinner during the week TPAC. (Large preview)

One of the nice things about TPAC (for those not chairing a working group or in some sort of leading role) was the ability to dip in and out of sessions. In amongst the things I attended over the few days I was at TPAC, there was a session from the Web Incubator community group (WICG), a developer meet-up with talks from prominent community members and demonstrations of new web technologies, and a Diversity and Inclusion for W3C meeting. An extra added bonus of going to TPAC with the Samsung Internet team was that we got to meet up with people from our team based in Korea, as well as other Samsung team members from the USA. 

How To Use Web Standards In Your Work

So, now that you know the why and wherefore of Web Standards, how do you go about using web standards in your work?

Mozilla Developer Network Web Docs (MDN Web Docs)

We (the Samsung Internet team) recommend that if you’re interested in learning more about a particular web standard or technology, you start with the MDN (Mozilla Developer Network) Web Docs. Whilst MDN WebDocs started as Mozilla Project, more recently it has become the place web developers go for cross-browser documentation on web platform technologies.


The MDN web docs homepage
The MDN Web Docs homepage (Large preview)

Last year, Samsung joined Bocoup, Google and Microsoft and W3C to form the MDN WebDocs Product Advisory Board to help ensure that MDN maintains this position.

When you search a technology in MDN, you will see a browser compatibility matrix letting you know what the browser support is. You will also find a link to the most relevant and up to date version of the standard. When you follow a link to a standard, you will be directed to the relevant web page outlining that standard and its technical specifications. These pages can be a little overwhelming at first, as they are somewhat ‘academic’ in structure. 

To give you some tips on navigating the documentation, let’s take a look at a standard I’m most familiar with: the W3C Web Content Accessibility Guidelines (2.1).


The Web Content Accessibility Guidelines (WCAG 2.1) web standard home page
The WCAG 2.1 web standard home page (Large preview)

This is the format of a W3C web standard. It features a table of contents on the left-hand side of the page while the content is organized into very structured headers — starting with the version, reports and editors details. These headers in standards are often used to quote the relevant parts of a standard “Oh, but WCAG 2.1 1.2.2 says”; but for those without the alphanumeric memory of a hard-disk, do not fear, it is not a requirement that you have to know these things by heart.

My first piece of advice about navigating web standards is to try not to be overwhelmed by these. If you’ve come from the non-academic route into web development like me, the structure of these documents can at first seem quite formal, and the language can feel this way, too. Don’t let this be a reason to navigate away from using this as a source of information  —  as quite frankly it is the best source of information available for finding out how and why web things work in the way that they do.

Here are some quick tips for working with web standards:

  • The TL;DR version
    Firstly, it’s important to understand that there isn’t a TL;DR for web standards. The reason they are these long and comprehensive documents is because they have to be. There can’t be any stone unturned when it comes to exacting the structure and expected us of web development things. However (a pro tip, and a way to avoid information overwhelm), is to start with the abstract of the standard and follow any links to introductory documents. In my example, the WCAG 2.1 standard document leads us to another linked page for the Web Content Accessibility Guidelines Overview. Which provides a range of useful documentation including a quick reference guide on how to meet WCAG 2

The homepage for the Web Content Accessibility Guidelines Overview
The homepage for the WCAG Overview (Large preview)
  • Make use the glossary of terms
    This just helps to understand the exact meaning of words and phrases in the context of the web standard . Let’s face it; there are so many terms out there with multiple meanings. Checking out the glossary also helps navigate some of the more academic terms.

The WCAG 2.1 Glossary section, which provides contextual definitions of words and phrases used within the standard.
The WCAG 2.1 Glossary section, which provides contextual definitions of words and phrases used within the standard. (Large preview)
  • ‘Find in page’ is your friend
    Once you have familiarized yourself with an overview and got an idea about the terms used within a web standard, you can start to search through the documentation for the information you require. The web standards are designed in such a way that you can consume them in a number of ways. If you seek to gain a comprehensive understanding then reading from start to finish is advised; however, you can also drop in and out of the sections as you require them. The good folks creating web standards have made efforts to ensure that referential content is linked to the source and any helpful resources are also included, which helps support the kind of “on demand” usage that is common. Take this example from WCAG 2.1:

WCAG 2.1 Guideline on Text Alternatives, in amongst the text are links to success criterions and other useful guidelines.
WCAG 2.1 Guideline on Text Alternatives, in amongst the text are links to success criterions and other useful guidelines. (Large preview)
  • If you’re not sure — ask!
    This community is put together from a bunch of people who care and have an investment in the future of web technologies. If you want to make sure you are adhering to Web Standards but maybe have got caught up in a language barrier, and you’re struggling to interpret what is meant by a phrase within a web standard, there are many folks out there that can help. You can raise issues through the W3C GitHub repositories for the W3C Web Standards or join the conversations about Web Standards through the suggested resources on the participate section of the W3C website.

How Do I Get Involved?

So, now that you know how to read up on your standards, what about getting involved? 

Well, here are a few places to start: 

  • GitHub repositories for standards
    The WC3, TC39, WhatWG and WICG all have organizations on GitHub that contain repositories for the work they are doing. Be sure to check in on the READme, contribution guidelines and code of conduct (if there is one) before you begin. Use the issues of a repository to look at what is currently being discussed in terms of future developments for the standard it relates to. 
  • The W3C website
    Here you can look at all the working groups, community groups, and forums. It is a great place to start; if you join the organization and become a member of a community group or working group you’ll be invited to the ongoing discussions, meetings, and events for that group.
  • The WhatWG website
    For all things WhatWG. Here there are guides on how to participate, FAQs, links to the GitHub repositories and a blog that is maintained by members of the WhatWG.
  • The WICG website
    Whilst the Web Incubator Community Group can be found from the W3C website, they are worth a separate shout-out here as they have their own web community page and Discourse instance. (For those of you not familiar with Discourse, it allows communities to create and maintain forums for discussion.)
  • The TC39 standard
    This is pretty comprehensive and includes links to the ways in which you can to contribute to the standard. 
  • Speak to Developer Advocates
    Many Web Developer Advocates are members of an SDO or known to be working on standards; teams like ours (the Samsung Internet Developer Advocates) are often involved in the work of Web Standards and happy to talk to developers that are interested in them. After all, standards have a huge impact on the future of the web and in turn the work that we do. So, depending on the web standard that interests you, you’ll be able to find folks like us (who are part of the work for those standards) through social media spaces like Twitter or Mastodon.

Thanks for reading! Remember that web standards impact everyone that builds or consumes websites, so the work of Web Standards is something we should all care about.

If you want to chat more about web standards, accessibility on the web, web audio or open-source adventures  —  you can find me on Twitter and I’m also on Mastodon. ✨

A huge thanks to Daniel Appelquist, who helped bring this article together.

Smashing Editorial
(ra, il)

Source: Smashing Magazine, Web Standards: The What, The Why, And The How

When And How To Use CSS Multi-Column Layout

dreamt up by webguru in Uncategorized | Comments Off on When And How To Use CSS Multi-Column Layout

When And How To Use CSS Multi-Column Layout

When And How To Use CSS Multi-Column Layout

Rachel Andrew



In all of the excitement about CSS Grid Layout and Flexbox, another layout method is often overlooked. In this article I’m going to take a look at Multi-column Layout — often referred to as multicol or sometimes “CSS Columns”. You’ll find out which tasks it is suited for, and some of the things to watch out for when making columns.

What Is Multicol?

The basic idea of multicol, is that you can take a chunk of content and flow it into multiple columns, as in a newspaper. You do this by using one of two properties. The column-count property specifies the number of columns that you would like the content to break into. The column-width property specifies the ideal width, leaving the browser to figure out how many columns will fit.

It doesn’t matter which elements are inside the content that you turn into a multicol container, everything remains in normal flow, but broken into columns. This makes multicol unlike other layout methods that we have in browsers today. Flexbox and Grid for example, take the child elements of the container and those items then participate in a flex or grid layout. With multicol, you still have normal flow, except inside a column.

In the below example I am using column-width, to display columns of at least 14em. Multicol assigns as many 14em columns as will fit and then shares out the remaining space between the columns. Columns will be at least 14em, unless we can only display one column in which case it may be smaller. Multicol was the first time that we saw this kind of behavior in CSS, columns being created which were essentialy responsive by default. You do not need to add Media Queries and change the number of columns for various breakpoints, instead we specify an optimal width and the browser will work it out.

See the Pen Smashing Multicol: column-width by Rachel Andrew (@rachelandrew) on CodePen.

Styling Columns

The column boxes created when you use one of the column properties can’t be targeted. You can’t address them with JavaScript, nor can you style an individual box to give it a background color or adjust the padding and margins. All of the column boxes will be the same size. The only thing you can do is add a rule between columns, using the column-rule property, which acts like border. You can also control the gap between columns using the column-gap property, which has a default value of 1em however you can change it to any valid length unit.

See the Pen Smashing Multicol: column styling by Rachel Andrew (@rachelandrew) on CodePen.

That is the basic functionality of multicol. You can take a chunk of content and split it into columns. Content will fill the columns in turn, creating columns in the inline direction. You can control the gaps between columns and add a rule, with the same possible values as border. So far so good, and all of the above is very well supported in browsers and has been for a long time, making this spec very safe to use in terms of backwards compatibility.

There are some further things you might want to consider with your columns, and some potential issues to be aware of when using columns on the web.

Spanning Columns

Sometimes you might like to break some content into columns, but then cause one element to span across the column boxes. Applying the column-span property to a descendent of the multicol container achieves this.

In the example below, I have caused a <blockquote> element to span across my columns. Note that when you do this, the content breaks into a set of boxes above the span, then starts a new set of column boxes below. The content doesn’t jump across the spanned element.

The column-span property is currently being implemented in Firefox and is behind a feature flag.

See the Pen Smashing Multicol: column-span by Rachel Andrew (@rachelandrew) on CodePen.

Be aware that in the current spec, the values for column-span are either all or none. You can’t span just some of the columns, but you can get the kind of layout you might see in a newspaper by combining multicol with other layout methods. In this next example, I have a grid container with two column tracks. The left-hand track is 2fr, the right-hand track 1fr. The article in the left-hand track I have turned into a multicol container with two tracks, it also has a spanning element.

On the right, we have an aside which goes into the second Grid column track. By playing around with the various layout methods available to us, we can figure out exactly which layout method suits the job at hand — don’t be afraid to mix and match!

See the Pen Smashing Multicol: mixing layout methods by Rachel Andrew (@rachelandrew) on CodePen.

Controlling Content Breaks

If you have content containing headings, then you probably want to avoid the situation where a heading ends up as the last thing in a column with the content going into the next column. If you have images with captions then the ideal situation would be for the image and caption to stay as one unit, not becoming split across columns. To deal with these problems CSS has properties to control where the content breaks.

When you split your content into columns, you perform what is known as fragmentation. The same is true if you split your content between pages, such as when you create a stylesheet for a print context. Therefore, multicol is closest to Paged Media than it is to other layout methods on the web. Because of this, for several years the way to control breaks in the content has been to use the page-break- properties which were part of CSS2.1.

  • page-break-before
  • page-break-after
  • page-break-inside

More recently the CSS Fragmentation specification has defined properties for fragmentation which are designed for any fragmented context, the spec includes details for Paged Media, multicol and the stalled Regions spec; Regions also fragments a continuous piece of content. By making these properties generic they can apply to any future fragmented context to, in the same way that the alignment properties from Flexbox were moved into the Box Alignment spec in order that they could be used in Grid and Block layout.

  • break-before
  • break-after
  • break-inside

As an example, I have used break-inside avoid on the <figure> element, to prevent the caption being detached from the image. A supporting browser should keep the figure together even if that causes the columns to look unbalanced.

See the Pen Smashing Multicol: break-inside by Rachel Andrew (@rachelandrew) on CodePen.

Unfortunately, support for these properties in multicol is pretty patchy. Even where supported they should be seen as a suggestion due to the fact that it would be possible to make so many requests while trying to control breaking, that essentially the browser can’t really break anywhere. The spec does define priorities in this case, however it is probably more useful for you to control only the most important cases.

The Problem Of Columns On the Web

One reason why we don’t see multicol used much on the web is the fact that it would be very easy to end up with a reading experience which made the reader scroll in the block dimension. That would mean scrolling up and down vertically for those of us using English or another vertical writing mode. This is not a good reading experience!

If you fix the height of the container, for example by using the viewport unit vh, and there is too much content, then overflow will happen in the inline direction and so you get a horizontal scrollbar instead.

See the Pen Smashing Multicol: overflow columns by Rachel Andrew (@rachelandrew) on CodePen.

Neither of these things are ideal, and making use of multicol on the web is something we need to think about very carefully in terms of the amount of content we might be aiming to flow into our columns.

Block Overflow Columns

For Level 2 of the specification, we are considering how to enable a method by which overflow columns, those which currently end up causing the horizontal scrollbar, could instead be created in the block direction. This would mean that you could have a multicol container with a height, and once the content had made columns which filled that container, a new set of columns would be created below. This would look a little like our spanning example above, however, instead of having a spanner causing the new column boxes to start, it would be the overflow caused by a container with a restriction in the block dimension.

This feature would make multicol far more useful for the web. While we are a little way off right now, you can keep an eye on the issue in the CSS Working Group repo. If you have additional use cases for this feature do post them, it can really help when designing the new feature.

What Is Multicol Useful For Today?

With the current specification, splitting all of your content into columns without consideration for the problems of scrolling isn’t advised. However, there are some cases where multicol is ideal on the web. There are enough uses cases to make it something you should consider when looking at design patterns.

Collapsing Small UI Or Text Elements

Multicol can be useful in any place where you have a small list of items that you want to take up less space. For example a simple listing of checkboxes, or a list of names. Often in these scenarios, the visitor is not reading down one column and then going back to the top of the next, but scanning the content for a checkbox to click or an item to select. Even if you do create a scrolled experience, it may not be an issue.

You can see an example of multicol used in this way by Sander de Jong on the DonarMuseum
website.


Names are arranged into multiple columns on the DonarMuseum website
On DonarMuseum, we can see multicol used to display lists of names. (Image source) (Large preview)

Known Small Amounts Of Content

There are times when we design a site where we know that some piece of content is relatively small, and will fit on the majority of screens without causing unwanted scrolling. I’ve used multicol on Notist presentation pages, for the introduction to a talk.

Andy Clarke designed a lovely example for the Equfund website.


A two-column layout including various content
On the Equfund website, you can see how different HTML elements remain in normal flow while displayed inside columns. (Image source) (Large preview)

To avoid the possibility of very small screens causing scrolling, remember that you can use media queries to check for height as well as width (or in a logical world, block as well as inline). If you only enable columns at a breakpoint which has a min-height large enough for their content, this can save users of very small devices having a poor scrolling experience.

Masonry-Like Display Of Content

Another place where Multiple-column Layout works beautifully is if you want to create a Masonry type of display of content. Multicol is the only layout method that will currently create this kind of layout with unequal height items. Grid would either leave a gap, or stretch the items to make a strict two-dimensional grid.

Veerle Pieters has a beautiful example of using multicol in this way on her inspiration page.


An arrangement of multiple boxes in columns designed by Veerle Pieters
In this design by Veerle Pieters, multicol is used to layout multiple boxes or cards as columns. (Large preview)

Grid And Flexbox Fallbacks

The column- properties can also be used as a fallback for Grid and Flex layout. If you specify one of the properties on a container, then turn that container into a Flex or Grid layout by using display: flex or display: grid any column behavior will be removed. If you have, for example, a cards layout that uses Grid Layout, and the layout would be readable if it ran in columns rather than across the page, you could use multicol as a simple fallback. Browsers that do not support Grid will get the multicol display, those which support Grid will get the Grid Layout.

Don’t Forget Multicol!

Fairly frequently I answer Grid and Flexbox questions where the answer is to not use Grid or Flexbox, but instead to look at Multicol. You probably won’t use it on every site, but when you come across a use case it can be really helpful. Over at MDN there are useful resources for multicol and the related fragmentation properties.

If you have used multicol on a project, perhaps drop a note in the comments, to share other ways in which we can use the feature.

Smashing Editorial
(il)

Source: Smashing Magazine, When And How To Use CSS Multi-Column Layout

Collective #482

dreamt up by webguru in Uncategorized | Comments Off on Collective #482


C482_DIVI

Our Sponsor

Divi: Just Click & Type

Divi comes with over 20 pre-made layouts right out of the box. These beautiful layouts can be used to jump-start your new projects, allowing you to build new pages in just a matter of minutes.

Check it out


C482_anime

Anime.js v3.0.0

The great animation library anime.js was updated with lots of new features, like spring easing, built-in staggering helper, new keyframes system, new callbacks and more.

Check it out






C482_dim

Dimensions.Guide

Dimensions.Guide is a comprehensive reference database of dimensioned drawings documenting the standard measurements and sizes of the everyday objects and spaces that make up our world.

Check it out











C482_Kawaii

React Kawaii

React Kawaii is a library of cute SVG illustrations that give some personality to your ReactJS application.

Check it out





Collective #482 was written by Pedro Botelho and published on Codrops.


Source: Codrops, Collective #482

Designing A Font Based On Old Handwriting

dreamt up by webguru in Uncategorized | Comments Off on Designing A Font Based On Old Handwriting

Designing A Font Based On Old Handwriting

Designing A Font Based On Old Handwriting

Carolyn Porter



Designers create handwriting-based connected cursive fonts for a variety of reasons: to immortalize the loops and swirls of a loved one’s handwriting, to digitize the penmanship of a person or document of historic significance, or to transform charming handwriting into a creative asset that can be licensed.

Let’s say you found a beautiful old handwriting specimen you want to digitize. You might presume you can trace individual letters, then seamlessly convert those tracings into a font. I will confess that was my assumption before I began to work on my first font. I had not taken into account the myriad of thoughtful and intentional decisions required to transform the specimen into an artful and functional font.

Before you begin the process of digitizing your specimen, it would be worthwhile to ask yourself a few questions about your goals and intent. Think of it as writing a creative brief for your project. Begin by assessing the importance of historical accuracy. Then conduct a close examination of the specimen: look at the idiosyncrasies in the handwriting, the variation in shape and position of individual letters, the method for connecting letters, and the texture. Possessing a keen familiarity of your specimen will allow you to make informed decisions about aesthetics as you design your font.

Recommended reading: Hands On The Sigmund Freud Typeface: Making A Font For Your Shrink

How Important Is Historical Accuracy?

One of the biggest decisions you will need to make is whether you want to capture every nuance of your handwriting specimen, or if you want to design something inspired by that handwriting. It is like watching a movie “based on a true story” versus one “inspired by real events.” In the first scenario, you can expect the movie (or font) maintains a higher degree of factual integrity than the second option, where the director (or designer) may take wide-ranging creative liberties.

If you choose to replicate your specimen with utmost precision, be aware that rigorously honoring accuracy may mean compromising legibility. “Old scripts, in particular, include letterforms that are less legible — even virtually illegible, like the old-style long s — than in modern handwriting,” notes Brian Willson, who has designed more than two dozen fonts based on the handwriting of notable figures such as Abigail Adams, Frederick Douglass, and Sam Houston.


Three examples of fonts based on, or inspired by, the handwriting of Abraham Lincoln.
Compare the similarities in these three fonts based on (or inspired by) the handwriting of Abraham Lincoln. Although 1863 Gettysburg (the font shown on the far right) was inspired by documents written by President Lincoln, the designer’s stated goal was not to replicate Lincoln’s exact writing, but to create a font that represented the era. (From left to right: LD Abe Lincoln by Lettering Delights/Illustration Ink, a Lincoln by Steve Woolf, and 1863 Gettysburg designed by GLC.) (Large preview)

You may find you want to make thoughtful revisions to strike a balance between historical accuracy and optimized legibility. As I designed the P22 Marcel Script, which is a connected cursive font based on handwritten WWII love letters, I chose to make small revisions to improve legibility.

The font retains the essential character of the original writing, but it is not a precise replica. Many of the original j’s, for example, did not have a tittle (a dot), and the original lowercase p did not have a closed curve on the bottom of the bowl. It looked like a hybrid between a p and an n. Knowing some people struggle to read cursive writing, I chose to dot the j and revise the shape of the p.


Three images showing original letter written by WWII French forced laborer Marcel Heuzé, a close-up of the greeting “Mes chères petites,” and the font P22 Marcel Script based on the handwriting sample shown.
Note the small revisions to improve legibility. (From left to right: The original handwriting specimen, a close-up of the greeting “Mes chères petites”, and a sample of the font P22 Marcel Script.) (Large preview)

Does Your Source Document Include Enough Material To Work With?

John Hancock had a fantastic signature, but designing an entire font based on the eight letters in his name — J, o, h, n, H, a, c, and k — would be a challenge. Assess whether your specimen is complete enough to support an entire font. Does it include both upper and lowercase letters? How about numbers?

When designing the font based on the handwriting of Jane Austen, designer Pia Frauss discovered she did not have a handwritten letter X to reference. If, like Frauss, you have a specimen that is mostly complete, you should be able to extrapolate what a specific letter might have looked like. That skill will also be necessary when it comes time to design new glyphs — the term for a specific character in a font file — like the Euro, which has only existed since 1999.

If your specimen only has a limited set of characters, gauge whether you feel comfortable designing the missing letters. If not, consider finding a handwritten specimen that is similar in style, then pulling the missing letters from the supplementary specimen.

What Idiosyncrasies Make The Handwriting Special?

Are crossbars unusually high, low, or angled? Are ascenders or descenders abnormally long or short? Are letters strikingly narrow or wide? Do specific letters extend above or below the baseline? Do letters loop in unusual ways?

If your goal is to create a historically accurate font, you will want to take care to ensure those idiosyncrasies are not lost as you digitize individual glyphs. If you are comfortable taking creative liberties, you might exaggerate those points of differentiation.

In the font Marydale, Brian Willson included quirky glyphs such as the loopy, two-story serif g found in the original handwriting. Brian thought it added friendly charm. But a risk of embracing idiosyncrasies is that some users may not like those letterforms — and indeed some users complained to Brian that the g was too unusual. Nevertheless, Marydale is one of Brian’s best-selling fonts.

To help you decode shapes and understand the mechanics behind some of your specimen’s idiosyncrasies, it may be helpful to identify the type of writing utensil that was used. A ballpoint pen will create uniform-width strokes; a split-nib pen can create graceful thicks and thins; a dip pen may carry evidence of ink being reapplied; a brush can create dramatic variation in thickness. You may also consider how the writing utensil had been held or if the writer had been in a hurry, since hand position and speed can influence the shape and style of the handwriting, too.

Assess Variations In Axis, Letter Height, Alignment To Baseline, And Stroke Width

Within any single handwriting specimen, you will likely see variation in axis, letter height, alignment to baseline, and stroke width. These irregularities enhance the individuality of handwriting — but transferring those irregularities to a digital font can be a challenge. If your font includes too many variations in axis, letter height, alignment to baseline, or stroke width, it may not reflect the visual unity of the original specimen. If your font does not include enough variation, it may lack the charm of the original writing.

Take time to assess which elements in the original specimen are consistent and which vary, then plan how you could incorporate those variations into your font. If you employ a consistent axis, consider varying alignment to baseline or stroke width. If you standardize stroke widths, consider varying axes and letter heights.


The word “Smashing” typeset in three different fonts that have three different levels of variability in stroke height, stroke width, and angle.
Fonts with increasing variability in axes, letter heights, alignment to baseline, and stroke thicknesses. (From left to right: Texas Hero by Brian Willson/Three Islands Press, P22 Cezanne Pro by Michael Want and James Grieshaber, and Selima by Jroh.) (Large preview)

When I began working on the P22 Marcel Script, I sourced favorite individual letters from five separate handwritten pages. The first time I pieced glyphs into words, I could see that the axes, letter heights, and stroke thicknesses were too variable. Even though each glyph had been a careful re-creation of one man’s handwriting, the resulting look was haphazard. I decided on a standard for axis and stroke thickness, then adjusted every glyph to that standard. Varying letter heights and alignment to baseline prevented the font from looking too mechanical.

Where And How Do Individual Letters Connect?

Are the connecting lines that sweep from one letter to the next high or low? Are the connecting lines thick or thin? Are there some letters that do not connect?

The key to designing a successfully connected cursive script font is to create an overlap so individual letters appear to seamlessly flow from one into the next. The trick is to identify one location for that overlap, then to start and end every glyph in that precise position. Some designers place those overlaps along the left-hand edge of a glyph, other designers place the overlap in the space between the glyphs. Some designers place the overlap low, others place the overlap high. There is no right or wrong answer; choose the location and method that makes sense for you.


Three detailed examples of ways glyphs in cursive script overlap to appear as if the letters connect in a fluid, seamless way.
Fonts that overlap on the left-hand edge of each glyph. The magenta circle shows the overlap; the horizontal magenta line shows the consistent placement of the sweeping connecting line on the letters m and a. (From left to right: Douglass Pen by Brian Willson/Three Islands Press, Rough Love by Positype, and Bambusa Pro by FontForecast.) (Large preview)

Three detailed examples of ways glyphs in cursive script overlap to appear as if the letters connect in a fluid, seamless way.
Fonts that overlap in the space between the glyphs. (From left to right: Dear Sarah Pro by Christian Robertson, Storyteller Script by My Creative Land, and Mila by Facetime.) (Large preview)

You may also discover that not all letters in your specimen connect. In that case, you will still need to identify one location and implement a consistent strategy for the overlaps, though you will only create the overlap on those glyphs that connect.* *


The word “Smashing” typeset in three different fonts where not all letters connect with the surrounding letters.
Fonts where some, but not all, letters connect. (From left to right: Blooms by DearType, JoeHand 2 by JOEBOB Graphics, and Brush Marker by Fenotype.) (Large preview)

Do You Want Your Font To Include A Texture Effect?

Adding texture can enhance a feeling of antiquity or nostalgia, but this treatment adds time, complexity, and increases file size. And it may influence whether or not someone will want to license and use your font, since they may or may not be looking for that specific effect.

Examine your original specimen to determine where the texture came from. Was it caused by the paper surface? Was variation caused by the writing tool or writing speed? Are irregularities clustered on curves? Does one side of the letter include more texture than the other? Do brush strokes or splatters of ink extend into the space surrounding each letter?


The word “Smashing” typeset in three different fonts that have three different textured looks: a paper texture look, rough edges, and brush strokes.
The word “Smashing” typeset in three different fonts that have three different textured looks: a paper texture look, rough edges, and brush strokes. (From left to right: Paper texture; Thirsty Rough by Yellow Design Studio, Scratchy pen and ink texture, Azalea Rough by Laura Worthington; Brush strokes, Sveglia by Wacaksara Co.) (Large preview)

Once you’ve made high-level decisions on the importance of historical accuracy, identified what idiosyncrasies make the handwriting special, considered how to add variation in axis, letter height, alignment to baseline, or stroke width, assessed how to connect glyphs, and decided if you want to include texture, it’s time to forge ahead with the design of your font.

Pick Your Letters

Make a copy of your specimen, and go through it line by line, flagging the specific characters you want to incorporate into your font. When designer Brian Willson begins a new font, it is not unusual for him to spend hours poring over the source material to select the individual letters he plans to include.

Consider flagging two types of letters:

  1. Favorite “workhorse” letters
    Workhorse letters will make up your basic glyph set. They might not be the fanciest option, but workhorse characters will make for a reliable and legible glyph set.

  2. Favorite swash letters
    Swash letters might include extra loops or flourishes, more or less texture, greater variation in position above or below the baseline, or exaggerated features such as extra-long crossbars.

Swash letters may be less legible, or may only be appropriate for use in specific instances, but can add variety, beauty, and personality. (Swash glyphs will be added later in the process; focus first on designing the workhorse glyph set.)

Ideally, at the end of your review, you will have flagged a complete set of upper and lowercase workhorse letters, numbers, punctuation marks, and an array of swash characters.

Scan Individual Letters And Prepare To Vectorize

Create high-resolution bitmap images of all letters you plan to include in your font. I have always used a flatbed scanner to capture those images, but I have heard of people exporting sketches from Procreate or taking high-resolution photos on their phones. If you take a photo, be sure your phone is parallel to your specimen to avoid distortion.

Once you have assembled a collection of bitmap images, you will need to choose between vectorizing the letterforms using Adobe Illustrator’s Image Trace feature or importing the scans directly into font editing software then vectorizing them by hand.

Using Illustrator’s Image Trace feature may be preferred if your specimen includes lots of texture since Illustrator can capture that texture for you. To create a vector outline, import a bitmap image into Illustrator, then using advanced Image Trace menu options, test combinations of Paths, Corners, and Noise to get the tracing result you prefer. Expand to get your vector outline.

Importing scans directly into font editing software may be preferred if your font is not going to include texture, if you are comfortable generating Bezier lines, or if you intend to make significant revisions to letter shapes.

Recommended reading: How New Font Technologies Will Improve The Web

Establish A New Font File

Popular font editing software options include FontLab Studio VI (Mac or Windows OS, $459), Glyphs (Mac OS, €249.90), Robofont (Mac OS, $490), Font Creator (Windows, $79–199), and Font Forge (free). There is also an extension for Illustrator and Photoshop CC called FontSelf which allows you to convert lettering into a font ($49–$98).

Establish a new font file. If you created vector outlines using Illustrator, import each outline into the applicable glyph cell (that is, place the vector outline of the letter a in the a glyph cell so that an a appears when you hit the a key on your keyboard).

If you chose to vectorize the glyphs within the font editing software, import each bitmap scan as a background sketch, then trace.

Identify Archetype Letters

Some font designers begin by designing or refining the letters n, b, o, v, A, H and O since those letters contain clues to the shape of many other letters. The n, for example, holds clues to the shape of the i and h; the b holds clues to the shape of d, p, and q; the O holds clues to C and G. Other designers begin with the most frequently used letters of the alphabet: e, t, a, o, i, and n. In the fantastic book Designing Type, which is chock full of images that compare variations in letter shapes, Karen Cheng notes some designers begin with the letters a, e, g, n, and o.

You may begin with one of those glyph sets, but if you’ve identified a few letters that quintessentially represent the aesthetic of your font, consider starting by refining those glyphs. When I began to work on the P22 Marcel Script, I began by working on the capital M for no other reason than the swoop of the original handwritten M was exquisite, and it brought joy to see that letter come to life as a glyph. (After working on the M, I focused on refining archetype letters n and e.)


Four images showing the original handwritten “M,” the original tracing, initial digitization, and the final letter “M” from the font P22 Marcel Script.
From left to right: Original scan, original tracing, glyph outline in FontLab (note the refinements to stroke width and axis), and final M in P22 Marcel Script. (Large preview)

No matter which glyphs you begin with, you will quickly want to establish standards for the axis, letter heights, alignment to baseline, and stroke widths. Ensure all glyphs meet those standards while simultaneously keeping in mind incorporating variability to achieve an organic look.

In order to introduce variability in an intentional way, it might be helpful to add guidelines to your workspace to define the lower and upper ranges of variability. Use these guidelines to introduce variation in ascender height, descender length, or in alignment to the baseline.

Do you want your font to have more variability? Increase the distance between the guidelines. Do you want your font to have less variability? Decrease the distance between the guidelines. Add variability to stroke widths in a similar way.


Lowercase a-z of connected cursive font P22 Marcel Script.
Lowercase glyph set of P22 Marcel Script. Notice variability in length of the descenders in the letters f, g, y, and z, variability in the height of letters i and j, and how I chose not to connect letters v and w with the following letter. (Large preview)

You will also want to establish a standard position for the overlap. Since there is not one correct place for these overlaps, experiment with a limited number of glyphs until the overlaps appear as natural as if the letters had been written with a pen without lifting the pen off the page. Then, test the position of the overlap on tricky letters such as r, o, s, f, v and w to confirm the overlap works. You’ll know if there are issues because glyphs won’t connect, or the connection won’t appear smooth. (If you see white where two black letters overlap, check for a Postscript path direction error.)

The good news is that once you have established the successful position for your overlaps you should be able to cut, copy, and paste the connecting line to replicate the position of the overlap on all remaining letters.


The word “Smashing” typeset in P22 Marcel Script with small circles highlighting small overlapping areas between letters.
Overlaps in P22 Marcel Script (Large preview)

Test As You Go

As soon as you have a collection of glyphs — even if it isn’t the entire alphabet — generate a test file and preview your font. If your goal was to replicate your specimen with accuracy, assess whether the font reflects the rhythm and character of the original handwriting. Evaluate whether the “color” — the overall darkness or lightness — matches the original. Refine glyphs as necessary to achieve the right rhythm, character, and color.

If you have chosen to take liberties with the shapes of letters or to introduce variability, your goal should still be to achieve an overall cohesive aesthetic. It is up to you as the designer to define precisely what that means.

As you test, it will be helpful to print blocks of sample text at a range of sizes. Reverse letters out of black. Look at the spaces between letters. Look at the spaces inside of letters. Look at strings of glyphs backwards, then upside down. Look at the font both when printed on paper and on a computer monitor. Testing under different conditions will help you notice glyphs that need additional refinement. I found that when I tested sample text set in a foreign language the unfamiliar letter combinations would help me see individual glyphs that were too heavy, too light, too narrow or too wide, along with individual curves that seemed too rounded or too flat.

For the first-time type designer, I recommend the book Inside Paragraphs: Typographic Fundamentals by Cyrus Highsmith (see list of references below). The book provides an invaluable primer on learning how to look at shapes and spaces in and around letters.

Continue testing and revising glyphs until your font includes a–z lowercase, A–Z uppercase, numbers, fractions, punctuation marks, and diacritics (marks such as the umlaut, acute, or tilde added to letters to indicate stress or change in pronunciation).

Add Swashes And Alternate Characters

Once your workhorse glyph set is complete, consider adding the swash characters you flagged when you picked your initial letters. A digital font can never offer the infinite variability found in handwriting, but by writing lines of OpenType code and incorporating swashes, ligatures (two or more letters that are combined into a single glyph), and alternate characters, you can begin to close the gap between the mechanical nature of a font and organic variation of handwriting. OpenType code allows you to do things like ensure two of the same glyphs never appear next to each other, or replace a workhorses glyph with a fancy swash or with a glyph that has more (or less) texture.

This work can be time-consuming, but you just might find it is addictive. You might discover every word you test could benefit from some custom flourish. The font Suomi, designed by Tomi Haaparanta, includes more than 700 ligatures. The font Hipster Script, designed by Ale Paul has 1,066. Syys Script by Julia Sysmäläinen for Art. Lebedev Studio has more than 2,000 glyphs. And between the Latin and Cyrillic versions, the font NIVEA Care Type by Juliasys has more than 4,000 glyphs.

Between swashes, ornaments, and alternate characters, the Pro version of the P22 Marcel Script includes more than 1,300 glyphs. Many of the alternate glyphs were inspired by flourishes in the original specimen; other glyphs were of my own invention, but were made in the style of the original writing. In my experience, incorporating swashes, ligatures, and alternate characters is the most exciting part about designing a connected cursive script font. In fact, it is what brings a connected cursive script font to life.


Four variations of the glyphs in the font P22 Marcel Script, beginning with the basic  “workhorse” glyphs progressing to variations with lots of flourishes.
From left to right: P22 Marcel Script basic (workhorse) glyph set, P22 Marcel Script Pro incorporating alternate glyphs, and P22 Marcel Script Pro showing additional alternate glyphs, swashes, and an ornament. (Large preview)

Final Steps

Once all your glyphs have been designed and the font has been thoroughly tested for technical performance and aesthetics, it is time to name the font and release it into the world.

Ensure no other font is already using the name you are considering. You can do a preliminary search on aggregator websites such as MyFonts, Fonts.com, FontShop, or Creative Market. Fonts are also distributed by individual font foundries and designers. Because there are so many distribution channels, the only way to guarantee availability and protect a name is to apply for a copyright with the U.S. Patent and Trademark Office (for U.S. designers). Consider hiring a lawyer to help with the filing process.

Finally, when it is time to release the font, if this is your first font it may be easiest to distribute your font through an established foundry or aggregator website. They should offer technical support, and will track licensing and sales tax. Consider working with one of the websites listed above; each website will have a different process to submit a font for consideration.

Further Reading

Smashing Editorial
(ah, ra, il)

Source: Smashing Magazine, Designing A Font Based On Old Handwriting

Powerful Image Analysis With Google Cloud Vision And Python

dreamt up by webguru in Uncategorized | Comments Off on Powerful Image Analysis With Google Cloud Vision And Python

Powerful Image Analysis With Google Cloud Vision And Python

Powerful Image Analysis With Google Cloud Vision And Python

Bartosz Biskupski



Quite recently, I’ve built a web app to manage user’s personal expenses. Its main features are to scan shopping receipts and extract data for further processing. Google Vision API turned out to be a great tool to get a text from a photo. In this article, I will guide you through the development process with Python in a sample project.

If you’re a novice, don’t worry. You will only need a very basic knowledge of this programming language — with no other skills required.

Let’s get started, shall we?

Never Heard Of Google Cloud Vision?

It’s an API that allows developers to analyze the content of an image through extracted data. For this purpose, Google utilizes machine learning models trained on a large dataset of images. All of that is available with a single API request. The engine behind the API classifies images, detects objects, people’s faces, and recognizes printed words within images.

To give you an example, let’s bring up the well-liked Giphy. They’ve adopted the API to extract caption data from GIFs, what resulted in significant improvement in user experience. Another example is realtor.com, which uses the Vision API’s OCR to extract text from images of For Sale signs taken on a mobile app to provide more details on the property.

Machine Learning At A Glance

Let’s start with answering the question many of you have probably heard before — what is the Machine Learning?

The broad idea is to develop a programmable model that finds patterns in the data its given. The higher quality data you deliver and the better the design of the model you use, the smarter outcome will be produced. With ‘friendly machine learning’ (as Google calls their Machine Learning through API services), you can easily incorporate a chunk of Artificial Intelligence into your applications.

Recommended reading: Getting Started With Machine Learning

How To Get Started With Google Cloud

Let’s start with the registration to Google Cloud. Google requires authentication, but it’s simple and painless — you’ll only need to store a JSON file that’s including API key, which you can get directly from the Google Cloud Platform.

Download the file and add it’s path to environment variables:

export GOOGLE_APPLICATION_CREDENTIALS=/path/to/your/apikey.json

Alternatively, in development, you can support yourself with the from_serivce_account_json() method, which I’ll describe further in this article. To learn more about authentication, check out Cloud’s official documentation.

Google provides a Python package to deal with the API. Let’s add the latest version of google-cloud-vision==0.33 to your app. Time to code!

How To Combine Google Cloud Vision With Python

Firstly, let’s import classes from the library.

from google.cloud import vision
from google.cloud.vision import types

When that’s taken care of, now you’ll need an instance of a client. To do so, you’re going to use a text recognition feature.

client = vision.ImageAnnotatorClient()

If you won’t store your credentials in environment variables, at this stage you can add it directly to the client.

client = vision.ImageAnnotatorClient.from_service_account_file(
'/path/to/apikey.json'
)

Assuming that you store images to be processed in a folder ‘images’ inside your project catalog, let’s open one of them.


Image of receipt that could be processed by Google Cloud Vision
An example of a simple receipt that could be processed by Google Cloud Vision. (Large preview)
image_to_open = 'images/receipt.jpg'

with open(image_to_open, 'rb') as image_file:
    content = image_file.read()

Next step is to create a Vision object, which will allow you to send a request to proceed with text recognition.

image = vision.types.Image(content=content)

text_response = client.text_detection(image=image)

The response consists of detected words stored as description keys, their location on the image, and a language prediction. For example, let’s take a closer look at the first word:

[
...
description: "SHOPPING"
bounding_poly {
  vertices {
    x: 1327
    y: 1513
  }
  vertices {
    x: 1789
    y: 1345
  }
  vertices {
    x: 1821
    y: 1432
  }
  vertices {
    x: 1359
    y: 1600
  }
}
...
]

As you can see, to filter text only, you need to get a description “on all the elements”. Luckily, with help comes Python’s powerful list comprehension.

texts = [text.description for text in text_response.text_annotations]

['SHOPPING STOREnREG 12-21n03:22 PMnCLERK 2n618n1 MISCn1 STUFFn$0.49n$7.99n$8.48n$0.74nSUBTOTALnTAXnTOTALnCASHn6n$9. 22n$10.00nCHANGEn$0.78nNO REFUNDSnNO EXCHANGESnNO RETURNSn', 'SHOPPING', 'STORE', 'REG', '12-21', '03:22', 'PM', 'CLERK', '2', '618', '1', 'MISC', '1', 'STUFF', '$0.49', '$7.99', '$8.48', '$0.74', 'SUBTOTAL', 'TAX', 'TOTAL', 'CASH', '6', '$9.', '22', '$10.00', 'CHANGE', '$0.78', 'NO', 'REFUNDS', 'NO', 'EXCHANGES', 'NO', 'RETURNS']

If you look carefully, you can notice that the first element of the list contains all text detected in the image stored as a string, while the others are separated words. Let’s print it out.

print(texts[0])

SHOPPING STORE
REG 12-21
03:22 PM
CLERK 2
618
1 MISC
1 STUFF
$0.49
$7.99
$8.48
$0.74
SUBTOTAL
TAX
TOTAL
CASH
6
$9. 22
$10.00
CHANGE
$0.78
NO REFUNDS
NO EXCHANGES
NO RETURNS

Pretty accurate, right? And obviously quite useful, so let’s play more.

What Can You Get From Google Cloud Vision?

As I’ve mentioned above, Google Cloud Vision it’s not only about recognizing text, but also it lets you discover faces, landmarks, image properties, and web connections. With that in mind, let’s find out what it can tell you about web associations of the image.

web_response = client.web_detection(image=image)

Okay Google, do you actually know what is shown on the image you received?

web_content = web_response.web_detection
web_content.best_guess_labels
>>> [label: "Receipt"]

Good job, Google! It’s a receipt indeed. But let’s give you a bit more exercise — can you see anything else? How about more predictions expressed in percentage?

predictions = [
(entity.description, '{:.2%}'.format(entity.score))) for entity in web_content.web_entities
]

>>> [('Receipt', '70.26%'), ('Product design', '64.24%'), ('Money', '56.54%'), ('Shopping', '55.86%'), ('Design', '54.62%'), ('Brand', '54.01%'), ('Font', '53.20%'), ('Product', '51.55%'), ('Image', '38.82%')]

Lots of valuable insights, well done, my almighty friend! Can you also find out where the image comes from and whether it has any copies?

web_content.full_matching_images
 >>> [
url: "http://www.rcapitalassociates.com/wp-content/uploads/2018/03/receipts.jpg", 
url:"https://media.istockphoto.com/photos/shopping-receipt-picture-id901964616?k=6&m=901964616&s=612x612&w=0&h=RmFpYy9uDazil1H9aXkkrAOlCb0lQ-bHaFpdpl76o9A=", 
url: "https://www.pakstat.com.au/site/assets/files/1172/shutterstock_573065707.500x500.jpg"
]

I’m impressed. Thanks, Google! But one is not enough, can you please give me three examples of similar images?

web_content.visually_similar_images[:3]
>>>[
url: "https://thumbs.dreamstime.com/z/shopping-receipt-paper-sales-isolated-white-background-85651861.jpg", 
url: "https://thumbs.dreamstime.com/b/grocery-receipt-23403878.jpg", 
url:"https://image.shutterstock.com/image-photo/closeup-grocery-shopping-receipt-260nw-95237158.jpg"
]

Sweet! Well done.

Is There Really An Artificial Intelligence In Google Cloud Vision?

As you can see in the image below, dealing with receipts can get a bit emotional.


Man screaming and looking stressed while holding a long receipt
An example of stress you can experience while getting a receipt. (Large preview)

Let’s have a look at what the Vision API can tell you about this photo.

image_to_open = 'images/face.jpg'

with open(image_to_open, 'rb') as image_file:
    content = image_file.read()
image = vision.types.Image(content=content)

face_response = client.face_detection(image=image)
face_content = face_response.face_annotations

face_content[0].detection_confidence
>>> 0.5153166651725769

Not too bad, the algorithm is more than 50% sure that there is a face in the picture. But can you learn anything about the emotions behind it?

face_content[0]
>>> [
...
joy_likelihood: VERY_UNLIKELY
sorrow_likelihood: VERY_UNLIKELY
anger_likelihood: UNLIKELY
surprise_likelihood: POSSIBLE
under_exposed_likelihood: VERY_UNLIKELY
blurred_likelihood: VERY_UNLIKELY
headwear_likelihood: VERY_UNLIKELY
...
]

Surprisingly, with a simple command, you can check the likeliness of some basic emotions as well as headwear or photo properties.

When it comes to the detection of faces, I need to direct your attention to some of the potential issues you may encounter. You need to remember that you’re handing a photo over to a machine and although Google’s API utilizes models trained on huge datasets, it’s possible that it will return some unexpected and misleading results. Online you can find photos showing how easily artificial intelligence can be tricked when it comes to image analysis. Some of them can be found funny, but there is a fine line between innocent and offensive mistakes, especially when a mistake concerns a human face.

With no doubt, Google Cloud Vision is a robust tool. Moreover, it’s fun to work with. API’s REST architecture and the widely available Python package make it even more accessible for everyone, regardless of how advanced you are in Python development. Just imagine how significantly you can improve your app by utilizing its capabilities!

Recommended reading: Applications Of Machine Learning For Designers

How Can You Broaden Your Knowledge On Google Cloud Vision

The scope of possibilities to apply Google Cloud Vision service is practically endless. With Python Library available, you can utilize it in any project based on the language, whether it’s a web application or a scientific project. It can certainly help you bring out deeper interest in Machine Learning technologies.

Google documentation provides some great ideas on how to apply the Vision API features in practice as well as gives you the possibility to learn more about the Machine Learning. I especially recommend to check out the guide on how to build an advanced image search app.

One could say that what you’ve seen in this article is like magic. After all, who would’ve thought that a simple and easily accessible API is backed by such a powerful, scientific tool? All that’s left to do is write a few lines of code, unwind your imagination, and experience the boundless potential of image analysis.

Smashing Editorial
(rb, ra, il)

Source: Smashing Magazine, Powerful Image Analysis With Google Cloud Vision And Python

How To Design Search For Your Mobile App

dreamt up by webguru in Uncategorized | Comments Off on How To Design Search For Your Mobile App

How To Design Search For Your Mobile App

How To Design Search For Your Mobile App

Suzanne Scacca



Why is Google the search behemoth it is today? Part of the reason is because of how it’s transformed our ability to search for answers.

Think about something as simple as looking up the definition of a word. 20 years ago, you would’ve had to pull your dictionary off the shelf to find an answer to your query. Now, you open your phone or turn on your computer, type or speak the word, and get an answer in no time at all and with little effort on your part.

This form of digital shortcutting doesn’t just exist on search engines like Google. Mobile apps now have self-contained search functions as well.

Is a search bar even necessary in a mobile app interface or is it overkill? Let’s take a look at why the search bar element is important for the mobile app experience. Then, we’ll look at a number of ways to design search based on the context of the query and the function of the app.

Using The Web With A Screen Reader

Did you know that VoiceOver makes up 11.7% of desktop screen reader users and rises to 69% of screen reader users on mobile? It’s important to know what sort of first-hand difficulties visually impaired users face and what web developers can do to help. Read article →

Mobile App Search Is Non-Negotiable

The search bar has been a standard part of websites for years, but statistics show that it isn’t always viewed as a necessity by users. This data from Neil Patel and Kissmetrics focuses on the perception and usage of the search bar on e-commerce websites:


Kissmetrics site search infographic
Data from a Kissmetrics infographic about site search. (Source: Kissmetrics) (Large preview)

As you can see, 60% of surveyed users prefer using navigation instead of search while 47% opt for filterable “search” over regular search functionality.

On a desktop website, this makes sense. When a menu is well-designed and well-labeled — no matter how extensive it may be — it’s quite easy to use. Add to that advanced filtering options, and I can see why website visitors would prefer that to search.

But mobile app users are a different breed. They go to mobile apps for different reasons than they do websites. In sum, they want a faster, concentrated, and more convenient experience. However, since smartphone screens have limited space, it’s not really feasible to include an expansive menu or set of filters to aid in the navigation of an app.

This is why mobile apps need a search bar.

You’re going to find a lot of use for search in mobile apps:

  • Content-driven apps like newspapers, publishing platforms, and blogs;
  • e-Commerce shops with large inventories and categorization of those inventories;
  • Productivity apps that contain documents, calendars, and other searchable records;
  • Listing sites that connect users to the right hotel, restaurant, itinerary, item for sale, apartment for rent, and so on;
  • Dating and networking apps that connect users with vast quantities of “matches”.

There are plenty more reasons why you’d need to use a search bar on your mobile app, but I’m going to let the examples below speak for themselves.

Ways To Design Search For Your Mobile App

I’m going to break down this next section into two categories:

  1. How to design the physical search element in your mobile app,
  2. How to design the search bar and its results within the context of the app.

1. Designing The Physical Search Element

There are a number of points to consider when it comes to the physical presence of your app search element:

Top Or Bottom?

Shashank Sahay explains why there are two places where the search element appears on a mobile app:

  • 1. Full-width bar at the top of the app.
    This is for apps that are driven by search. Most of the time, users open the app with the express purpose of conducting a search.

Facebook app search
Facebook prioritizes app search by placing it at the top. (Source: Facebook) (Large preview)

Facebook is a good example. Although Facebook users most likely do engage with the news feed in the app, I have a sneaking suspicion that Facebook’s data indicates that the search function is more commonly engaged with — at least in terms of first steps. Hence, why it’s placed at the top of the app.

  • 2. A tab in the bottom-aligned navigation bar.
    This is for apps that utilize search as an enhancement to the primary experience of using the app’s main features.

Let’s contrast Facebook against one of its sister properties: Instagram. Unlike Facebook, Instagram is a very simple social media app. Users follow other accounts and get glimpses into the content they share through full-screen story updates as well as from inside their endless-scroll news feed.


Instagram app search
Instagram places its search function in the bottom navigation bar. (Source: Instagram) (Large preview)

With that said, the search function does exist in the navigation bar so that users can look up other accounts to peruse through or follow.

As far as this basic breakdown goes, Sahay is right about how placement of search correlates with intention. But the designing of the search element goes beyond just where it’s placed on the app.

Shallow Or Deep?

There will be times when a mobile app would benefit from a search function deep within the app experience.

You’ll see this sort of thing quite often in e-commerce apps like Bed Bath & Beyond:


Bed Bath & Beyond app search
Bed Bath & Beyond uses deep search to help users find nearby stores (Source: Bed Bath & Beyond) (Large preview)

In this example, this search function exists outside of the standard product search on the main landing page. Results for this kind of search are also displayed in a unique way which is reflective of the purpose of the search:


Bed Bath & Beyond map search results
Bed Bath & Beyond displays search results on a map. (Source: Bed Bath & Beyond) (Large preview)

There are other ways you use might need to use “deep” search functions on e-commerce apps.

Think about stores that have loads of comments attached to each product. If your users want to zero in on what other consumers had to say about a product (for example, if a camping tent is waterproof), the search function would help them quickly get to reviews containing specific keywords.

You’ll also see deep searches planted within travel and entertainment apps like Hotels.com:


Hotels.com app search
Hotels.com includes a deep search to narrow down results by property name. (Source: Hotels.com) (Large preview)

You’re all probably familiar with the basic search function that goes with any travel-related app. You enter the details of your trip and it pulls up the most relevant results in a list or map format. That’s what this screenshot is of.

However, see where it says “Property Name” next to the magnifying glass? This is a search function within a search function. And the only things users can search for here are actual hotel property names.

Bar, Tab, Or Magnifying Glass?

This brings me to my next design point: how to know which design element to represent the search function with.

You’ve already seen clear reasons to use a full search bar over placing a tab in the navigation bar. But how about a miniaturized magnifying glass?

Here’s an example of how this is used in the YouTube mobile app:


YouTube app search icon
YouTube uses a magnifying glass to represent its search function. (Source: YouTube) (Large preview)

The way I see it, the magnifying glass is the search design element you’d use when:

  • One of the primary reasons users come to the app is to do a search,
  • And it competes against another primary use case.

In this case, YouTube needs the mini-magnifying glass because it serves two types of users:

  1. Users that come to the app to search for videos.
  2. Users that come to the app to upload their own videos.

To conserve space, links to both exist within the header of the YouTube app. If you have competing priorities within your app, consider doing the same.

“Search” Or Give A Hint?

One other thing to think about when designing search for mobile apps is the text inside the search box. To decide this, you have to ask yourself:

“Will my users know what sort of stuff they can look up with this search function?”

In most cases they will, but it might be best to include hint text inside the search bar just to make sure you’re not adding unnecessary friction. Here’s what I mean by that:

This is the app for Airbnb:


Airbnb app search text
Airbnb offers hint text to guide users to more accurate search results. (Source: Airbnb) (Large preview)

The search bar tells me to “Try ‘Costa de Valencia’”. It’s not necessarily an explicit suggestion. It’s more helping me figure out how I can use this search bar to research places to stay on an upcoming trip.

For users that are new to Airbnb, this would be a helpful tip. They might come to the site thinking it’s like Hotels.com that enables users to look up things like flights and car rentals. Airbnb, instead, is all about providing lodging and experiences, so this search text is a good way to guide users in the right direction and keep them from receiving a “Sorry, there are no results that match your query” response.

2. Designing The Search Bar And Results In Context

Figuring out where to place the search element is one point to consider. Now, you have to think about how to present the results to your mobile app users:

Simple Search

This is the most basic of the search functions you can offer. Users type their query into the search bar. Relevant results appear below. In other words, you leave it up to your users to know what they’re searching for and to enter it correctly.

When a relevant query is entered, you can provide results in a number of ways.

For an app like Flipboard, results are displayed as trending hashtags:


Flipboard app search results
Flipboard displays search results as a list of hashtags. (Source: Flipboard) (Large preview)

It’s not the most common way you’d see search results displayed, but it makes sense in this particular context. What users are searching for are categories of content they want to see in their feed. These hashtagged categories allow users to choose high-level topics that are the most relevant to them.

ESPN has a more traditional basic search function:


ESPN app search results
ESPN has designed its search results in a traditional list. (Source: ESPN) (Large preview)

As you can see, ESPN provides a list of results that contain the keyword. There’s nothing more to it than that though. As you’ll see in the following examples, you can program your app search to more closely guide users to the results they want to see.

Filtered Search

According to the aforementioned Kissmetrics survey, advanced filtering is a popular search method among website users. If your mobile app has a lot of content or a vast inventory of products, consider adding filters to the end of your search function to improve the experience further. Your users are already familiar with the search technique. Plus, it’ll save you the trouble of having to add advancements to the search functionality itself.

Yelp has a nice example of this:


Yelp app search filters
Yelp users have filter options available after doing a search. (Source: Yelp) (Large preview)

In the search above, I originally looked for restaurants in my “Current Location”. Among the various filters displayed, I decided to add “Order Delivery” to my query. My search query then became:

Restaurants > Current Location > Delivery

This is really no different than using breadcrumbs on a website. In this case, you let users do the initial work by entering a search query. Then, you give them filters that allow them to narrow down their search further.

Again, this is another way to reduce the chances that users will encounter the “No results” response to their query. Because filters correlate to actual categories and segmentations that exist within the app, you can ensure they end up with valid search results every time.

e-Commerce websites are another good use case for filters. Here is how Wayfair does this:


Wayfair app search filters
Wayfair includes filters in search to help users narrow down results. (Source: Wayfair) (Large preview)

Wayfair’s list of search results is fairly standard for an e-commerce marketplace. The number of items are displayed, followed by a grid of matching product images and summary details.

Here’s the thing though: Wayfair has a massive inventory. It’s the same with other online marketplaces like Amazon and Zappos. So, when you tell users that their search query produced 2,975 items, you need a way to mitigate some of the overwhelm that may come with that.

By placing the Sort and Filter buttons directly beside the search result total, you’re encouraging users to do a little more work on their search query to ensure they get the best and most relevant results.

Predictive Search

Autocomplete is something your users are already familiar with. For apps that contain lots of content, utilizing this type of search functionality could be majorly helpful to your users.

For one, they already know how it works and so they won’t be surprised when related query suggestions appear before them. In addition, autocomplete offers a sort of personalization. As you gather more data on a user as well as the kinds of searches they conduct, autocomplete anticipates their needs and provides a shortcut to the desired content.

Pinterest is a social media app that people use to aggregate content they’re interested in and to seek out inspiration for pretty much anything they’re doing in life:


Pinterest app search autocomplete
Pinterest anticipates users’ search queries and provides autocomplete shortcuts. (Source: Pinterest) (Large preview)

Take a look at the search results above. Can you tell what I’ve been thinking about lately? The first is how I’m going to decorate my new apartment. The second is my next tattoo. And despite only typing out the word “Small”, Pinterest immediately knew what’s been top-of-mind with me as of recent. That doesn’t necessarily mean I as a user came to the app with that specific intention today… but it’s nice to see that personalized touch as I engage with the search bar.

Another app I engage with a lot is the Apple Photos app:


Apple Photos app search
Apple Photos uses autocomplete to help users find the most relevant photos. (Source: Apple) (Large preview)

In addition to using it to store all of my personal photos, I use this on a regular basis to take screenshots for work (as I did in this article). As you can imagine, I have a lot of content saved to this app and it can be difficult finding what I need just by scrolling through my folders.

In the example above, I was trying to find a photo I had taken at Niagara Falls, but I couldn’t remember if I had labeled it as such. So, I typed in “water” and received some helpful autocomplete suggestions on “water”-related words as well as photos that fit the description.

I would also put “Recent Search” results into this bucket. Here’s an example from Uber:


Uber app recent search results
Uber’s recent search results provide one-click shortcuts to repeat users. (Source: Uber) (Large preview)

Before I even had a chance to type my search query in the Uber app, it displays my most recent search queries for me.

I think this would be especially useful for people who use ride-sharing services on a regular basis. Think about professionals who work in a city. Rather than own a car, they use Uber to transport to and from their office as well as client appointments. By providing a shortcut to recent trips in search results, the Uber app cuts down the time they spend booking a trip.

If you have enough data on your users and you have a way to anticipate their needs, autocomplete is a fantastic way to personalize search and improve the overall experience.

Limited Search

I think this time savings point is an important one to remember when designing search for mobile apps.

Unlike websites where longer times-on-page matter, that’s not always the case with mobile apps. Unless you’ve built a gaming or news app where users should spend lots of time engaging with the app on a daily basis, it’s not usually the amount of time spent inside the app that matters.

Your goal in building a mobile app is to retain users over longer periods, which means providing a meaningful experience while they’re inside it. A well-thought-out search function will greatly contribute to this as it gets users immediately to what they want to see, even if it means they leave the app just a few seconds later.

If you have an app that needs to get users in and out of it quickly, think about limiting search results as Ibotta has done:


Ibotta app search categories
Ibotta displays categories that users can search in. (Source: Ibotta) (Large preview)

While users certainly can enter any query they’d like, Ibotta makes it clear that the categories below are the only ones available to search from. This serves as both a reminder of what the app is capable of as well as a means for circumventing the search results that don’t matter to users.

Hotels.com also places limits on its search function:


Hotels.com limiting search results
Hotels.com forces users to make a choice so they don’t end up with too many results. (Source: Hotels.com) (Large preview)

As you can see here, users can’t just look for hotels throughout the country of Croatia. It’s just too broad of a search and one that Hotels.com shouldn’t have to provide. For one, it’s probably too taxing on the Hotels.com server to execute a query of that nature. Plus, it would provide a terrible experience for users. Imagine how many hotels would show up in that list of results.

By reining in what your users can search for and the results they can see, you can improve the overall experience while shortening the time it takes them to convert.

Wrapping Up

As you can see here, a search bar isn’t some throwaway design element. When your app promises a speedy and convenient experience to its users, a search bar can cut down on the time they have to spend inside it. It can also make the app a more valuable resource as it doesn’t require much work or effort to get to the desired content.

Smashing Editorial
(ra, yk, il)

Source: Smashing Magazine, How To Design Search For Your Mobile App

Front-End Performance Checklist 2019 [PDF, Apple Pages, MS Word]

dreamt up by webguru in Uncategorized | Comments Off on Front-End Performance Checklist 2019 [PDF, Apple Pages, MS Word]

Front-End Performance Checklist 2019 [PDF, Apple Pages, MS Word]

Front-End Performance Checklist 2019 [PDF, Apple Pages, MS Word]

Vitaly Friedman



Web performance is a tricky beast, isn’t it? How do we actually know where we stand in terms of performance, and what our performance bottlenecks exactly are? Is it expensive JavaScript, slow web font delivery, heavy images, or sluggish rendering? Is it worth exploring tree-shaking, scope hoisting, code-splitting, and all the fancy loading patterns with intersection observer, server push, clients hints, HTTP/2, service workers and — oh my — edge workers? And, most importantly, where do we even start improving performance and how do we establish a performance culture long-term?

Back in the day, performance was often a mere afterthought. Often deferred till the very end of the project, it would boil down to minification, concatenation, asset optimization and potentially a few fine adjustments on the server’s config file. Looking back now, things seem to have changed quite significantly.

Performance isn’t just a technical concern: it matters, and when baking it into the workflow, design decisions have to be informed by their performance implications. Performance has to be measured, monitored and refined continually, and the growing complexity of the web poses new challenges that make it hard to keep track of metrics, because metrics will vary significantly depending on the device, browser, protocol, network type and latency (CDNs, ISPs, caches, proxies, firewalls, load balancers and servers all play a role in performance).

So, if we created an overview of all the things we have to keep in mind when improving performance — from the very start of the process until the final release of the website — what would that list look like? Below you’ll find a (hopefully unbiased and objective) front-end performance checklist for 2019 — an updated overview of the issues you might need to consider to ensure that your response times are fast, user interaction is smooth and your sites don’t drain user’s bandwidth.

Table Of Contents

(You can also just download the checklist PDF (166 KB) or download editable Apple Pages file (275 KB) or the .docx file (151 KB). Happy optimizing, everyone!)

ol.start {
counter-reset: perfcounter;
}
ol.start > li:before, ol.continue > li:before {
content: counters(perfcounter, ‘.’, decimal-leading-zero);
counter-increment: perfcounter;
}

@media all and (min-width: 1024px) {
ol.start > li, ol.continue > li {
list-style: none;
margin-bottom: 1.5em;
margin-top: 2em;
}
ol.start > li:first-child, ol.continue > li:first-child {
margin-top: 1em;
}
ol.start > li:before, ol.continue > li:before {
margin-left: -1.1em;
margin-right: 2.4%;
font-family: “Mija”, Arial, sans-serif;
display: inline-block;
line-height: 1.1em;
text-align: center;
background-color: #E53B2C;
color: #fff;
padding: .65em .5em .5em .5em;
border-radius: 11px;
}
ol.start p, ol.continue p {
font-size: inherit;
}
}

Getting Ready: Planning And Metrics

Micro-optimizations are great for keeping performance on track, but it’s critical to have clearly defined targets in mind — measurable goals that would influence any decisions made throughout the process. There are a couple of different models, and the ones discussed below are quite opinionated — just make sure to set your own priorities early on.

  1. Establish a performance culture.
    In many organizations, front-end developers know exactly what common underlying problems are and what loading patterns should be used to fix them. However, as long as there is no established endorsement of the performance culture, each decision will turn into a battlefield of departments, breaking up the organization into silos. You need a business stakeholder buy-in, and to get it, you need to establish a case study on how speed benefits metrics and Key Performance Indicators (KPIs) they care about.

    Without a strong alignment between dev/design and business/marketing teams, performance isn’t going to sustain long-term. Study common complaints coming into customer service and see how improving performance can help relieve some of these common problems.

    Run performance experiments and measure outcomes — both on mobile and on desktop. It will help you build up a company-tailored case study with real data. Furthermore, using data from case studies and experiments published on WPO Stats will help increase sensitivity for business about why performance matters, and what impact it has on user experience and business metrics. Stating that performance matters alone isn’t enough though — you also need to establish some measurable and trackable goals and observe them.

    How to get there? In her talk on Building Performance for the Long Term, Allison McKnight shares a comprehensive case-study of how she helped establish a performance culture at Etsy (slides).


Brad Frost and Jonathan Fielding’s Performance Budget Calculator
Performance budget builder by Brad Frost and Jonathan Fielding’s Performance Budget Calculator can help you set up your performance budget and visualize it. (Large preview)
  1. Goal: Be at least 20% faster than your fastest competitor.
    According to psychological research, if you want users to feel that your website is faster than your competitor’s website, you need to be at least 20% faster. Study your main competitors, collect metrics on how they perform on mobile and desktop and set thresholds that would help you outpace them. To get accurate results and goals though, first study your analytics to see what your users are on. You can then mimic the 90th percentile’s experience for testing.

    To get a good first impression of how your competitors perform, you can use Chrome UX Report (CrUX, a ready-made RUM data set, video introduction by Ilya Grigorik), Speed Scorecard (also provides a revenue impact estimator), Real User Experience Test Comparison or SiteSpeed CI (based on synthetic testing).

    Note: If you use Page Speed Insights (no, it isn’t deprecated), you can get CrUX performance data for specific pages instead of just the aggregates. This data can be much more useful for setting performance targets for assets like “landing page” or “product listing”. And if you are using CI to test the budgets, you need to make sure your tested environment matches CrUX if you used CrUX for setting the target (thanks Patrick Meenan!).

    Collect data, set up a spreadsheet, shave off 20%, and set up your goals (performance budgets) this way. Now you have something measurable to test against. If you’re keeping the budget in mind and trying to ship down just the minimal script to get a quick time-to-interactive, then you’re on a reasonable path.

    Need resources to get started?

    Once you have a budget in place, incorporate them into your build process with Webpack Performance Hints and Bundlesize, Lightouse CI, PWMetrics or Sitespeed CI to enforce budgets on pull requests and provide a score history in PR comments. If you need something custom, you can use webpagetest-charts-api, an API of endpoints to build charts from WebPagetest results.

    For instance, just like Pinterest, you could create a custom eslint rule that disallows importing from files and directories that are known to be dependency-heavy and would bloat the bundle. Set up a listing of “safe” packages that can be shared across the entire team.

    Beyond performance budgets, think about critical customer tasks that are most beneficial to your business. Set and discuss acceptable time thresholds for critical actions and establish “UX ready” user timing marks that the entire organization has agreed on. In many cases, user journeys will touch on the work of many different departments, so alignment in terms of acceptable timings will help support or prevent performance discussions down the road. Make sure that additional costs of added resources and features are visible and understood.

    Also, as Patrick Meenan suggested, it’s worth to plan out a loading sequence and trade-offs during the design process. If you prioritize early on which parts are more critical, and define the order in which they should appear, you will also know what can be delayed. Ideally, that order will also reflect the sequence of your CSS and JavaScript imports, so handling them during the build process will be easier. Also, consider what the visual experience should be in “in-between”-states, while the page is being loaded (e.g. when web fonts aren’t loaded yet).

    Planning, planning, planning. It might be tempting to get into quick “low-hanging-fruits”-optimizations early on — and eventually it might be a good strategy for quick wins — but it will be very hard to keep performance a priority without planning and setting realistic, company-tailored performance goals.

The difference between First Paint, First Contentful Paint, First Meaningful Paint, Visual Complete and Time To Interactive. Large view. Credit: @denar90
  1. Choose the right metrics.
    Not all metrics are equally important. Study what metrics matter most to your application: usually it will be related to how fast you can start render most important pixels of your product and how quickly you can provide input responsiveness for these rendered pixels. This knowledge will give you the best optimization target for ongoing efforts.

    One way or another, rather than focusing on full page loading time (via onLoad and DOMContentLoaded timings, for example), prioritize page loading as perceived by your customers. That means focusing on a slightly different set of metrics. In fact, choosing the right metric is a process without obvious winners.

    Based on Tim Kadlec’s research and Marcos Iglesias’ notes in his talk, traditional metrics could be grouped into a few sets. Usually, we’ll need all of them to get a complete picture of performance, and in your particular case some of them might be more important than others.

    • Quantity-based metrics measure the number of requests, weight and a performance score. Good for raising alarms and monitoring changes over time, not so good for understanding user experience.
    • Milestone metrics use states in the lifetime of the loading process, e.g. Time To First Byte and Time To Interactive. Good for describing the user experience and monitoring, not so good for knowing what happens between the milestones.
    • Rendering metrics provide an estimate of how fast content renders (e.g. Start Render time, Speed Index). Good for measuring and tweaking rendering performance, but not so good for measuring when important content appears and can be interacted with.
    • Custom metrics measure a particular, custom event for the user, e.g. Twitter’s Time To First Tweet and Pinterest’s PinnerWaitTime. Good for describing the user experience precisely, not so good for scaling the metrics and comparing with with competitors.

    To complete the picture, we’d usually look out for useful metrics among all of these groups. Usually, the most specific and relevant ones are:

    • First Meaningful Paint (FMP)
      Provides the timing when primary content appears on the page, providing an insight into how quickly the server outputs any data. Long FMP usually indicates JavaScript blocking the main thread, but could be related to back-end/server issues as well.
    • Time to Interactive (TTI)
      The point at which layout has stabilized, key webfonts are visible, and the main thread is available enough to handle user input — basically the time mark when a user can interact with the UI. The key metrics for understanding how much wait a user has to experience to use the site without a lag.
    • First Input Delay (FID), or Input responsiveness
      The time from when a user first interacts with your site to the time when the browser is actually able to respond to that interaction. Complements TTI very well as it describes the missing part of the picture: what happens when a user actually interacts with the site. Intended as a RUM metric only. There is a JavaScript library for measuring FID in the browser.
    • Speed Index
      Measures how quickly the page contents are visually populated; the lower the score, the better. The Speed Index score is computed based on the speed of visual progress, but it’s merely a computed value. It’s also sensitive to the viewport size, so you need to define a range of testing configurations that match your target audience (thanks, Boris!).
    • CPU time spent
      A metric that indicates how busy is the main thread with the processing of the payload. It shows how often and how long the main thread is blocked, working on painting, rendering, scripting and loading. High CPU time is a clear indicator of a janky experience, i.e. when the user experiences a noticeable lag between their action and a response. With WebPageTest, you can select “Capture Dev Tools Timeline” on the “Chrome” tab to expose the breakdown of the main thread as it runs on any device using WebPageTest.
    • Ad Weight Impact
      If your site depends on the revenue generated by advertising, it’s useful to track the weight of ad related code. Paddy Ganti’s script constructs two URLs (one normal and one blocking the ads), prompts the generation of a video comparison via WebPageTest and reports a delta.
    • Deviation metrics
      As noted by Wikipedia engineers, data of how much variance exists in your results could inform you how reliable your instruments are, and how much attention you should pay to deviations and outlers. Large variance is an indicator of adjustments needed in the setup. It also helps understand if certain pages are more difficult to measure reliably, e.g. due to third-party scripts causing significant variation. It might also be a good idea to track browser version to understand bumps in performance when a new browser version is rolled out.
    • Custom metrics
      Custom metrics are defined by your business needs and customer experience. It requires you to identify important pixels, critical scripts, necessary CSS and relevant assets and measure how quickly they get delivered to the user. For that one, you can monitor Hero Rendering Times, or use Performance API, marking particular timestaps for events that are important for your business. Also, you can collect custom metrics with WebPagetest by executing arbitrary JavaScript at the end of a test.

    Steve Souders has a detailed explanation of each metric. It’s important to notice that while Time-To-Interactive is measured by running automated audits in the so-called lab environment, First Input Delay represents the actual user experience, with actual users experiencing a noticeable lag. In general, it’s probably a good idea to always measure and track both of them.

    Depending on the context of your application, preferred metrics might differ: e.g. for Netflix TV UI, key input responsiveness, memory usage and TTI are more critical, and for Wikipedia, first/last visual changes and CPU time spent metrics are more important.

    Note: both FID and TTI do not account for scrolling behavior; scrolling can happen independently since it’s off-main-thread, so for many content consumption sites these metrics might be much less important (thanks, Patrick!).



User-centric performance metrics provide a better insight into the actual user experience. First Input Delay (FID) is a new metric that tries to achieve just that. (Large preview)
  1. Gather data on a device representative of your audience.
    To gather accurate data, we need to thoroughly choose devices to test on. It’s a good option to choose a Moto G4, a mid-range Samsung device, a good middle-of-the-road device like a Nexus 5X and a slow device like Alcatel 1X, perhaps in an open device lab. For testing on slower thermal-throttled devices, you could also get a Nexus 2, which costs just around $100.

    If you don’t have a device at hand, emulate mobile experience on desktop by testing on a throttled network (e.g. 150ms RTT, 1.5 Mbps down, 0.7 Mbps up) with a throttled CPU (5× slowdown). Eventually switch over to regular 3G, 4G and Wi-Fi. To make the performance impact more visible, you could even introduce 2G Tuesdays or set up a throttled 3G network in your office for faster testing.

    Keep in mind that on a mobile device, you should be expecting a 4×–5× slowdown compared to desktop machines. Mobile devices have different GPUs, CPU, different memory, different battery characteristics. While download times are critical for low-end networks, parse times are critical for phones with slow CPUs. In fact, parse times on mobile are 36% higher than on desktop. So always test on an average device — a device that is most representative of your audience.

  2. Introducing the slowest day of the week
    Introducing the slowest day of the week. Facebook has introduced 2G Tuesdays to increase visibility and sensitivity of slow connections. (Image source)

    Luckily, there are many great options that help you automate the collection of data and measure how your website performs over time according to these metrics. Keep in mind that a good performance picture covers a set of performance metrics, lab data and field data:

    • Synthetic testing tools collect lab data in a reproducible environment with predefined device and network settings (e.g. Lighthouse, WebPageTest) and
    • Real User Monitoring (RUM) tools evaluate user interactions continuously and collect field data (e.g. SpeedCurve, New Relic — both tools provide synthetic testing, too).

    The former is particularly useful during development as it will help you identify, isolate and fix performance issues while working on the product. The latter is useful for long-term maintenance as it will help you understand your performance bottlenecks as they are happening live — when users actually access the site.

    By tapping into built-in RUM APIs such as Navigation Timing, Resource Timing, Paint Timing, Long Tasks, etc., synthetic testing tools and RUM together provide a complete picture of performance in your application. You could use PWMetrics, Calibre, SpeedCurve, mPulse and Boomerang, Sitespeed.io, which all are great options for performance monitoring. Furthermore, with Server Timing header, you could even
    monitor back-end and front-end performance all in one place.

    Note: It’s always a safer bet to choose network-level throttlers, external to the browser, as, for example, DevTools has issues interacting with HTTP/2 push, due to the way it’s implemented (thanks, Yoav, Patrick!). For Mac OS, we can use Network Link Conditioner, for Windows Windows Traffic Shaper, for Linux netem, and for FreeBSD dummynet.


Lighthouse
Lighthouse, a performance auditing tool integrated into DevTools. (Large preview)
  1. Set up “clean” and “customer” profiles for testing.
    While running tests in passive monitoring tools, it’s a common strategy to turn off anti-virus and background CPU tasks, remove background bandwidth transfers and test with a clean user profile without browser extensions to avoid skewed results (Firefox, Chrome).

    However, it’s also a good idea to study which extensions your customers are using frequently, and test with a dedicated “customer” profile as well. In fact, some extensions might have a profound performance impact on your application, and if your users use them a lot, you might want to account for it up front. “Clean” profile results alone are overly optimistic and can be crushed in real-life scenarios.

  2. Share the checklist with your colleagues.
    Make sure that the checklist is familiar to every member of your team to avoid misunderstandings down the line. Every decision has performance implications, and the project would hugely benefit from front-end developers properly communicating performance values to the whole team, so that everybody would feel responsible for it, not just front-end developers. Map design decisions against performance budget and the priorities defined in the checklist.

Setting Realistic Goals

  1. 100-millisecond response time, 60 fps.
    For an interaction to feel smooth, the interface has 100ms to respond to user’s input. Any longer than that, and the user perceives the app as laggy. The RAIL, a user-centered performance model gives you healthy targets: To allow for <100 milliseconds response, the page must yield control back to main thread at latest after every <50 milliseconds. Estimated Input Latency tells us if we are hitting that threshold, and ideally, it should be below 50ms. For high-pressure points like animation, it’s best to do nothing else where you can and the absolute minimum where you can’t.


    RAIL
    RAIL, a user-centric performance model.

    Also, each frame of animation should be completed in less than 16 milliseconds, thereby achieving 60 frames per second (1 second ÷ 60 = 16.6 milliseconds) — preferably under 10 milliseconds. Because the browser needs time to paint the new frame to the screen, your code should finish executing before hitting the 16.6 milliseconds mark. We’re starting having conversations about 120fps (e.g. iPad’s new screens run at 120Hz) and Surma has covered some rendering performance solutions for 120fps, but that’s probably not a target we’re looking at just yet.

    Be pessimistic in performance expectations, but be optimistic in interface design and use idle time wisely. Obviously, these targets apply to runtime performance, rather than loading performance.

  2. Speed Index < 1250, TTI < 5s on 3G, Critical file size budget < 170KB (gzipped).
    Although it might be very difficult to achieve, a good ultimate goal would be First Meaningful Paint under 1 second and a Speed Index value under 1250. Considering the baseline being a $200 Android phone (e.g. Moto G4) on a slow 3G network, emulated at 400ms RTT and 400kbps transfer speed, aim for Time to Interactive under 5s, and for repeat visits, aim for under 2s (achievable only with a service worker),

    Notice that, when speaking about interactivity metrics, it’s a good idea to distinguish between First CPU Idle and Time To Interactive to avoid misunderstandings down the line. The former is the earliest point after the main content has rendered (where there is at least a 5-second window where the page is responsive). The latter is the point where the page can be expected to always be responsive to input (thanks, Philip Walton!).

    We have two major constraints that effectively shape a reasonable target for speedy delivery of the content on the web. On the one hand, we have network delivery constraints due to TCP Slow Start. The first 14KB of the HTML is the >most critical payload chunk — and the only part of the budget that can be delivered in the first roundtrip (which is all you get in 1 sec at 400ms RTT due to mobile wake-up times).

    On the other hand, we have hardware constraints on memory and CPU due to JavaScript parsing times (we’ll talk about them in detail later). To achieve the goals stated in the first paragraph, we have to consider the critical file size budget for JavaScript. Opinions vary on what that budget should be (and it heavily depends on the nature of your project), but a budget of 170KB JavaScript gzipped already would take up to 1s to parse and compile on an average phone. Assuming that 170KB expands to 3× that size when decompressed (0.7MB), that already could be the death knell of a “decent” user experience on a Moto G4 or Nexus 2.

    Of course, your data might show that your customers are not on these devices, but perhaps they simply don’t show up in your analytics because your service is inaccessible to them due to slow performance. In fact, Google’s Alex Russels recommends to aim for 130–170KB gzipped as a reasonable upper boundary, and exceeding this budget should be an informed and deliberate decision. In real-life world, most products aren’t even close: an average bundle size today is around 400KB, which is up 35% compared to late 2015. On a middle-class mobile device, that accounts for 30-35 seconds for Time-To-Interactive.

    We could also go beyond the bundle size budget though. For example, we could set performance budgets based on the activities of the browser’s main thread, i.e. paint time before start render, or track down front-end CPU hogs. Tools such as Calibre, SpeedCurve and Bundlesize can help you keep your budgets in check, and can be integrated into your build process.

    Also, a performance budget probably shouldn’t be a fixed value. Depending on the network connection, performance budgets should adapt, but payload on slower connection is much more “expensive”, regardless of how they’re used.


From 'Fast By Default: Modern Loading Best Practices' by Addy Osmani
From Fast By Default: Modern loading best practices by Addy Osmani (Slide 19)


Performance budgets should adapt depending on the network conditions for an average mobile device. (Image source: Katie Hempenius) (Large preview)

Defining The Environment

  1. Choose and set up your build tools.
    Don’t pay too much attention to what’s supposedly cool these days. Stick to your environment for building, be it Grunt, Gulp, Webpack, Parcel, or a combination of tools. As long as you are getting results you need and you have no issues maintaining your build process, you’re doing just fine.

    Among the build tools, Webpack seems to be the most established one, with literally hundreds of plugins available to optimize the size of your builds. Getting started with Webpack can be tough though. So if you want to get started, there are some great resources out there:

  2. Use progressive enhancement as a default.
    Keeping progressive enhancement as the guiding principle of your front-end architecture and deployment is a safe bet. Design and build the core experience first, and then enhance the experience with advanced features for capable browsers, creating resilient experiences. If your website runs fast on a slow machine with a poor screen in a poor browser on a sub-optimal network, then it will only run faster on a fast machine with a good browser on a decent network.

  3. <!–

    If you need a practical implementation of the strategy on mid-scale and large-scale projects, Scott Jehl’s Modernizing our Progressive Enhancement Delivery article is a good place to start.

    –>

  4. Choose a strong performance baseline.
    With so many unknowns impacting loading — the network, thermal throttling, cache eviction, third-party scripts, parser blocking patterns, disk I/O, IPC latency, installed extensions, antivirus software and firewalls, background CPU tasks, hardware and memory constraints, differences in L2/L3 caching, RTTS — JavaScript has the heaviest cost of the experience, next to web fonts blocking rendering by default and images often consuming too much memory. With the performance bottlenecks moving away from the server to the client, as developers, we have to consider all of these unknowns in much more detail.

    With a 170KB budget that already contains the critical-path HTML/CSS/JavaScript, router, state management, utilities, framework and the application logic, we have to thoroughly examine network transfer cost, the parse/compile time and the runtime cost of the framework of our choice.

    As noted by Seb Markbåge, a good way to measure start-up costs for frameworks is to first render a view, then delete it and then render again as it can tell you how the framework scales. The first render tends to warm up a bunch of lazily compiled code, which a larger tree can benefit from when it scales. The second render is basically an emulation of how code reuse on a page affects the performance characteristics as the page grows in complexity.

'Fast By Default: Modern Loading Best Practices' by Addy Osmani
From Fast By Default: Modern Loading Best Practices by Addy Osmani (Slides 18, 19).
  1. Evaluate each framework and each dependency.
    Now, not every project needs a framework and not every page of a single-page-application needs to load a framework. In Netflix’s case, “removing React, several libraries and the corresponding app code from the client-side reduced the total amount of JavaScript by over 200KB, causing an over-50% reduction in Netflix’s Time-to-Interactivity for the logged-out homepage.” The team then utilized the time spent by users on the landing page to prefetch React for subsequent pages that users were likely to land on (read on for details).

    It might sound obvious but worth stating: some projects can also benefit benefit from removing an existing framework altogether. Once a framework is chosen, you’ll be staying with it for at least a few years, so if you need to use one, make sure your choice is informed and well considered.

    Inian Parameshwaran has measured performance footprint of top 50 frameworks (against First Contentful Paint — the time from navigation to the time when the browser renders the first bit of content from the DOM). Inian discovered that, out there in the wild, Vue and Preact are the fastest across the board — both on desktop and mobile, followed by React (slides). You could examine your framework candidates and the proposed architecture, and study how most solutions out there perform, e.g. with server-side rendering or client-side rendering, on average.

    Baseline performance cost matters. According to a study by Ankur Sethi, “your React application will never load faster than about 1.1 seconds on an average phone in India, no matter how much you optimize it. Your Angular app will always take at least 2.7 seconds to boot up. The users of your Vue app will need to wait at least 1 second before they can start using it.” You might not be targeting India as your primary market anyway, but users accessing your site with suboptimal network conditions will have a comparable experience. In exchange, your team gains maintainability and developer efficiency, of course. But this consideration needs to be deliberate.

    You could go as far as evaluating a framework (or any JavaScript library) on Sacha Greif’s 12-point scale scoring system by exploring features, accessibility, stability, performance, package ecosystem, community, learning curve, documentation, tooling, track record, team, compatibility, security for example. But on a tough schedule, it’s a good idea to consider at least the total cost on size + initial parse times before choosing an option; lightweight options such as Preact, Inferno, Vue, Svelte or Polymer can get the job done just fine. The size of your baseline will define the constraints for your application’s code.

    A good starting point is to choose a good default stack for your application. Gatsby.js (React), Preact CLI, and PWA Starter Kit provide reasonable defaults for fast loading out of the box on average mobile hardware.


JavaScript processing times in 2018 by Addy Osmani
(Image credit: Addy Osmani) (Large preview)
  1. Consider using PRPL pattern and app shell architecture.
    Different frameworks will have different effects on performance and will require different strategies of optimization, so you have to clearly understand all of the nuts and bolts of the framework you’ll be relying on. When building a web app, look into the PRPL pattern and application shell architecture. The idea is quite straightforward: Push the minimal code needed to get interactive for the initial route to render quickly, then use service worker for caching and pre-caching resources and then lazy-load routes that you need, asynchronously.
PRPL Pattern in the application shell architecture
PRPL stands for Pushing critical resource, Rendering initial route, Pre-caching remaining routes and Lazy-loading remaining routes on demand.
Application shell architecture
An application shell is the minimal HTML, CSS, and JavaScript powering a user interface.
  1. Have you optimized the performance of your APIs?
    APIs are communication channels for an application to expose data to internal and third-party applications via so-called endpoints. When designing and building an API, we need a reasonable protocol to enable the communication between the server and third-party requests. Representational State Transfer (REST) is a well-established, logical choice: it defines a set of constraints that developers follow to make content accessible in a performant, reliable and scalable fashion. Web services that conform to the REST constraints, are called RESTful web services.

    As with good ol’ HTTP requests, when data is retrieved from an API, any delay in server response will propagate to the end user, hence delaying rendering. When a resource wants to retrieve some data from an API, it will need to request the data from the corresponding endpoint. A component that renders data from several resources, such as an article with comments and author photos in each comment, may need several roundtrips to the server to fetch all the data before it can be rendered. Furthermore, the amount of data returned through REST is often more than what is needed to render that component.

    If many resources require data from an API, the API might become a performance bottleneck. GraphQL provides a performant solution to these issues. Per se, GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data. Unlike REST, GraphQL can retrieve all data in a single request, and the response will be exactly what is required, without over or under-fetching data as it typically happens with REST.

    In addition, because GraphQL is using schema (metadata that tells how the data is structured), it can already organize data into the preferred structure, so, for example, with GraphQL, we could remove JavaScript code used for dealing with state management, producing a cleaner application code that runs faster on the client.

    If you want to get started with GraphQL, Eric Baer published two fantastic articles on yours truly Smashing Magazine: A GraphQL Primer: Why We Need A New Kind Of API and A GraphQL Primer: The Evolution Of API Design (thanks for the hint, Leonardo!).


Hacker Noon
A difference between REST and GraphQL, illustrated via a conversation between Redux + REST on the left, an Apollo + GraphQL on the right. (Image source: Hacker Noon) (Large preview)
  1. Will you be using AMP or Instant Articles?
    Depending on the priorities and strategy of your organization, you might want to consider using Google’s AMP or Facebook’s Instant Articles or Apple’s Apple News. You can achieve good performance without them, but AMP does provide a solid performance framework with a free content delivery network (CDN), while Instant Articles will boost your visibility and performance on Facebook.

    The seemingly obvious benefit of these technologies for users is guaranteed performance, so at times they might even prefer AMP-/Apple News/Instant Pages-links over “regular” and potentially bloated pages. For content-heavy websites that are dealing with a lot of third-party content, these options could potentially help speed up render times dramatically.

    Unless they don’t. According to Tim Kadlec, for example, “AMP documents tend to be faster than their counterparts, but they don’t necessarily mean a page is performant. AMP is not what makes the biggest difference from a performance perspective.”

    A benefit for the website owner is obvious: discoverability of these formats on their respective platforms and increased visibility in search engines. You could build progressive web AMPs, too, by reusing AMPs as a data source for your PWA. Downside? Obviously, a presence in a walled garden places developers in a position to produce and maintain a separate version of their content, and in case of Instant Articles and Apple News without actual URLs (thanks Addy, Jeremy!).

  2. Choose your CDN wisely.
    Depending on how much dynamic data you have, you might be able to “outsource” some part of the content to a static site generator, pushing it to a CDN and serving a static version from it, thus avoiding database requests. You could even choose a static-hosting platform based on a CDN, enriching your pages with interactive components as enhancements (JAMStack). In fact, some of those generators (like Gatsby on top of React) are actually website compilers with many automated optimizations provided out of the box. As compilers add optimizations over time, the compiled output gets smaller and faster over time.

    Notice that CDNs can serve (and offload) dynamic content as well. So, restricting your CDN to static assets is not necessary. Double-check whether your CDN performs compression and conversion (e.g. image optimization in terms of formats, compression and resizing at the edge), support for servers workers, edge-side includes, which assemble static and dynamic parts of pages at the CDN’s edge (i.e. the server closest to the user), and other tasks.

    Note: based on research by Patrick Meenan and Andy Davies, HTTP/2 is effectively broken on many CDNs, so we shouldn’t be too optimistic about the performance boost there.

Assets Optimizations

  1. Use Brotli or Zopfli for plain text compression.
    In 2015, Google introduced Brotli, a new open-source lossless data format, which is now supported in all modern browsers. In practice, Brotli appears to be much more effective than Gzip and Deflate. It might be (very) slow to compress, depending on the settings, but slower compression will ultimately lead to higher compression rates. Still, it decompresses fast. You can also estimate Brotli compression savings for your site.

    Browsers will accept it only if the user is visiting a website over HTTPS though. What’s the catch? Brotli still doesn’t come preinstalled on some servers today, and it’s not straightforward to set up without self-compiling Nginx. Still, it’s not that difficult, and its support is coming, e.g. it’s available since Apache 2.4.26. Brotli is widely supported, and many CDNs support it (Akamai, AWS, KeyCDN, Fastly, Cloudlare, CDN77) and you can enable Brotli even on CDNs that don’t support it yet (with a service worker).

    At the highest level of compression, Brotli is so slow that any potential gains in file size could be nullified by the amount of time it takes for the server to begin sending the response as it waits to dynamically compress the asset. With static compression, however, higher compression settings are preferred.

    Alternatively, you could look into using Zopfli’s compression algorithm, which encodes data to Deflate, Gzip and Zlib formats. Any regular Gzip-compressed resource would benefit from Zopfli’s improved Deflate encoding because the files will be 3 to 8% smaller than Zlib’s maximum compression. The catch is that files will take around 80 times longer to compress. That’s why it’s a good idea to use Zopfli on resources that don’t change much, files that are designed to be compressed once and downloaded many times.

    If you can bypass the cost of dynamically compressing static assets, it’s worth the effort. Both Brotli and Zopfli can be used for any plaintext payload — HTML, CSS, SVG, JavaScript, and so on.

    The strategy? Pre-compress static assets with Brotli+Gzip at the highest level and compress (dynamic) HTML on the fly with Brotli at level 1–4. Make sure that the server handles content negotiation for Brotli or gzip properly. If you can’t install/maintain Brotli on the server, use Zopfli.

  2. Use responsive images and WebP.
    As far as possible, use responsive images with srcset, sizes and the <picture> element. While you’re at it, you could also make use of the WebP format (supported in Chrome, Opera, Firefox 65, Edge 18) by serving WebP images with the <picture> element and a JPEG fallback (see Andreas Bovens’ code snippet) or by using content negotiation (using Accept headers). Ire Aderinokun has a very detailed tutorial on converting images to WebP, too.

    Sketch natively supports WebP, and WebP images can be exported from Photoshop using a WebP plugin for Photoshop. Other options are available, too. If you’re using WordPress or Joomla, there are extensions to help you easily implement support for WebP, such as Optimus and Cache Enabler for WordPress and Joomla’s own supported extension (via Cody Arsenault).

    It’s important to note that while WebP image file sizes compared to equivalent Guetzli and Zopfli, the format doesn’t support progressive rendering like JPEG, which is why users might see an actual image faster with a good ol’ JPEG although WebP images might get faster through the network. With JPEG, we can serve a “decent” user experience with the half or even quarter of the data and load the rest later, rather than have a half-empty image as it is in the case of WebP. Your decision will depend on what you are after: with WebP, you’ll reduce the payload, and with JPEG you’ll improve perceived performance.

    On Smashing Magazine, we use the postfix -opt for image names — for example, brotli-compression-opt.png; whenever an image contains that postfix, everybody on the team knows that the image has already been optimized. And — shameless plug! — Jeremy Wagner even published a Smashing book on WebP.

Responsive Image Breakpoints Generator
The Responsive Image Breakpoints Generator automates images and markup generation.
  1. Are images properly optimized?
    When you’re working on a landing page on which it’s critical that a particular image loads blazingly fast, make sure that JPEGs are progressive and compressed with mozJPEG (which improves the start rendering time by manipulating scan levels) or Guetzli, Google’s new open-source encoder focusing on perceptual performance, and utilizing learnings from Zopfli and WebP. The only downside: slow processing times (a minute of CPU per megapixel). For PNG, we can use Pingo, and for SVG, we can use SVGO or SVGOMG. And if you need to quickly preview and copy or download all the SVG assets from a website, svg-grabber can do that for you, too.

    Every single image optimization article would state it, but keeping vector assets clean and tight is always worth reminding. Make sure to clean up unused assets, remove unnecessary metadata and reduces the amount of path points in artwork (and thus SVG code). (Thanks, Jeremy!)

    There are more advanced options though. You could:

    • Use Squoosh to compress, resize and manipulate images at the optimal compression levels (lossy or lossless),
    • Use the Responsive Image Breakpoints Generator or a service such as Cloudinary or Imgix to automate image optimization. Also, in many cases, using srcset and sizes alone will reap significant benefits.
    • To check the efficiency of your responsive markup, you can use imaging-heap, a command line tool that measure the efficiency across viewport sizes and device pixel ratios.
    • Lazy load images and iframes with lazysizes, a library that detects any visibility changes triggered through user interaction (or IntersectionObserver which we’ll explore later).
    • Watch out for images that are loaded by default, but might never be displayed — e.g. in carousels, accordions and image galleries.
    • Consider Swapping Images with the Sizes Attribute by specifying different image display dimensions depending on media queries, e.g. to manipulate sizes to swap sources in a magnifier component.
    • Review image download inconsistencies to prevent unexpected downloads for foreground and background images.
    • To optimize storage interally, you could use Dropbox’s new Lepton format for losslessly compressing JPEGs by an average of 22%.
    • Watch out for the aspect-ratio property in CSS and intrinsicsize attribute which will allow us to set aspect ratios and dimensions for images, so browser can reserve a pre-defined layout slot early to avoid layout jumps during the page load.
    • If you feel adventurous, you could chop and rearrange HTTP/2 streams using Edge workers, basically a real-time filter living on the CDN, to send images faster through the network. Edge workers use JavaScript streams that use chunks which you can control (basically they are JavaScript that runs on the CDN edge that can modify the streaming responses), so you can control the delivery of images. With service worker it’s too late as you can’t control what’s on the wire, but it does work with Edge workers. So you can use them on top of static JPEGs saved progressively for a particular landing page.


    A sample output by imaging-heap, a command line tool that measure the efficiency across viewport sizes and device pixel ratios. (Image source) (Large preview)

    The future of responsive images might change dramatically with the adoption of client hints. Client hints are HTTP request header fields, e.g. DPR, Viewport-Width, Width, Save-Data, Accept (to specify image format preferences) and others. They are supposed to inform the server about the specifics of user’s browser, screen, connection etc. As a result, the server can decide how to fill in the layout with appropriately sized images, and serve only these images in desired formats. With client hints, we move the resource selection from HTML markup and into the request-response negotiation between the client and server.

    As Ilya Grigorik noted, client hints complete the picture — they aren’t an alternative to responsive images. “The <picture> element provides the necessary art-direction control in the HTML markup. Client hints provide annotations on resulting image requests that enable resource selection automation. Service Worker provides full request and response management capabilities on the client.” A service worker could, for example, append new client hints headers values to the request, rewrite the URL and point the image request to a CDN, adapt response based on connectivity and user preferences, etc. It holds true not only for image assets but for pretty much all other requests as well.

    For clients that support client hints, one could measure 42% byte savings on images and 1MB+ fewer bytes for 70th+ percentile. On Smashing Magazine, we could measure 19-32% improvement, too. Unfortunately, client hints still have to gain some browser support. Under
    consideration in Firefox and
    Edge. However, if you supply both the normal responsive images markup and the <meta> tag for Client Hints, then the browser will evaluate the responsive images markup and request the appropriate image source using the Client Hints HTTP headers.

    Not good enough? Well, you can also improve perceived performance for images with the multiple background images technique. Keep in mind that playing with contrast and blurring out unnecessary details (or removing colors) can reduce file size as well. Ah, you need to enlarge a small photo without losing quality? Consider using Letsenhance.io.

    These optimizations so far cover just the basics. Addy Osmani has published a very detailed guide on Essential Image Optimization that goes very deep into details of image compression and color management. For example, you could blur out unnecessary parts of the image (by applying a Gaussian blur filter to them) to reduce the file size, and eventually you might even start removing colors or turn the picture into black and white to reduce the size even further. For background images, exporting photos from Photoshop with 0 to 10% quality can be absolutely acceptable as well. Ah, and don’t use JPEG-XR on the web — “the processing of decoding JPEG-XRs software-side on the CPU nullifies and even outweighs the potentially positive impact of byte size savings, especially in the context of SPAs”.

  2. Are videos properly optimized?
    We covered images so far, but we’ve avoided a conversation about good ol’ GIFs. Frankly, instead of loading heavy animated GIFs which impact both rendering performance and bandwidth, it’s a good idea to switch either to animated WebP (with GIF being a fallback) or replace them with looping HTML5 videos altogether. Yes, the browser performance is slow with <video>, and, unlike with images, browsers do not preload <video> content, but they tend to be lighter and smaller than GIFs. Not an option? Well, at least we can add lossy compression to GIFs with Lossy GIF, gifsicle or giflossy.

    Early tests show that inline videos within img tags display 20× faster and decode 7× faster than the GIF equivalent, in addition to being a fraction in file size. Although the support for <img src=".mp4"> has landed in Safari Technology Preview, we are far from it being adopted widely as it’s not coming to Blink any time soon.

    Addy Osmani recommends to replace animated GIFs with looping inline videos. The file size difference is noticeable (80% savings). (Large preview)

    In the land of good news though, video formats have been advancing massively over the years. For a long time, we had hoped that WebM would become the format to rule them all, and WebP (which is basically one still image inside of the WebM video container) will become a replacement for dated image formats. But despite WebP and WebM gaining support these days, the breakthrough didn’t happen.

    In 2018, the Alliance of Open Media has released a new promising video format called AV1. AV1 has compression similar to H.265 codec (the evolution of H.264) but unlike the latter, AV1 is free. The H.265 license pricing pushed browser vendors to adopting a comparably performant AV1 instead: AV1 (just like H.265) compress twice as good as WebP.


    AV1 Logo 2018
    AV1 has good chances of becoming the ultimate standard for video on the web. (Image credit: Wikimedia.org) (Large preview)

    In fact, Apple currently uses HEIF format and HEVC (H.265), and all the photos and videos on the latest iOS are saved in these formats, not JPEG. While HEIF and HEVC (H.265) aren’t properly exposed to the web (yet?), AV1 is — and it’s gaining browser support. So adding the AV1 source in your <video> tag is reasonable, as all browser vendors seem to be on board.

    For now, the most widely used and supported encoding is H.264, served by MP4 files, so before serving the file, make sure that your MP4s are processed with a multipass-encoding, blurred with the frei0r iirblur effect (if applicable) and moov atom metadata is moved to the head of the file, while your server accepts byte serving. Boris Schapira provides exact instructions for FFmpeg to optimize videos to the maximum. Of course, providing WebM format as an alternative would help, too.

    Video playback performance is a story on its own, and if you’d like to dive into it in details, take a look at Doug Sillar’s series on The Current State of Video and Video Delivery Best Practices that include details on video delivery metrics, video preloading, compression and streaming.

Zach Leatherman’s Comprehensive Guide to Font-Loading Strategies

Zach Leatherman’s Comprehensive Guide to Font-Loading Strategies provides a dozen options for better web font delivery.
  1. Are web fonts optimized?
    The first question that’s worth asking if you can get away with using UI system fonts in the first place. If it’s not the case, chances are high that the web fonts you are serving include glyphs and extra features and weights that aren’t being used. You can ask your type foundry to subset web fonts or if you are using open-source fonts, subset them on your own with Glyphhanger or Fontsquirrel. You can even automate your entire workflow with Peter Müller’s subfont, a command line tool that statically analyses your page in order to generate the most optimal web font subsets, and then inject them into your page.

    WOFF2 support is great, and you can use WOFF as fallback for browsers that don’t support it — after all, legacy browsers would probably be served well enough with system fonts. There are many, many, many options for web font loading, and you can choose one of the strategies from Zach Leatherman’s “Comprehensive Guide to Font-Loading Strategies,” (code snippets also available as Web font loading recipes).

    Probably the better options to consider today are Critical FOFT with preload and “The Compromise” method. Both of them use a two-stage render for delivering web fonts in steps — first a small supersubset required to render the page fast and accurately with the web font, and then load the rest of the family async. The difference is that “The Compromise” technique loads polyfill asynchronously only if font load events are not supported, so you don’t need to load the polyfill by default. Need a quick win? Zach Leatherman has a quick 23-min tutorial and case study to get your fonts in order.

    In general, it’s a good idea to use the preload resource hint to preload fonts, but in your markup include the hints after the link to critical CSS and JavaScript. Otherwise, font loading will cost you in the first render time. Still, it might be a good idea to be selective and choose files that matter most, e.g. the ones that are critical for rendering or that would help you avoiding visible and disruptive text reflows. In general, Zach advises to preload one or two fonts of each family — it also makes sense to delay some font loading if they are less-critical.

    Nobody likes waiting for the content to be displayed. With the font-display CSS descriptor, we can control the font loading behavior and enable content to be readable immediately (font-display: optional) or almost immediately (font-display: swap). However, if you want to avoid text reflows, we still need to use the Font Loading API, specifically to group repaints, or when you are using third party hosts. Unless you can use Google Fonts with Cloudflare Workers, of course. Talking about Google Fonts: consider using google-webfonts-helper, a hassle-free way to self-host Google Fonts. Always self-host your fonts for maximum control if you can.

    In general, if you use font-display: optional, it might not be a good idea to also use preload as it it’ll trigger that web font request early (causing network congestion if you have other critical path resources that need to be fetched). Use preconnect for faster cross-origin font requests, but be cautious with preload as preloading fonts from a different origin wlll incur network contention. All of these techniques are covered in Zach’s Web font loading recipes.

    Also, it might be a good idea to opt out of web fonts (or at least second stage render) if the user has enabled Reduce Motion in accessibility preferences or has opted in for Data Saver Mode (see Save-Data header). Or when the user happens to have slow connectivity (via Network Information API).

    To measure the web font loading performance, consider the All Text Visible metric (the moment when all fonts have loaded and all content is displayed in web fonts), as well as Web Font Reflow Count after first render. Obviously, the lower both metrics are, the better the performance is. It’s important to notice that variable fonts might require a significant performance consideration. They give designers a much broader design space for typographic choices, but it comes at the cost of a single serial request opposed to a number of individual file requests. That single request might be slow blocking the entire typographic appearance on the page. On the good side though, with a variable font in place, we’ll get exactly one reflow by default, so no JavaScript will be required to group repaints.

    Now, what would make a bulletproof web font loading strategy? Subset fonts and prepare them for the 2-stage-render, declare them with a font-display descriptor, use Font Loading API to group repaints and store fonts in a persistent service worker’s cache. You could fall back to Bram Stein’s Font Face Observer if necessary. And if you’re interested in measuring the performance of font loading, Andreas Marschke explores performance tracking with Font API and UserTiming API.

    Finally, don’t forget to include unicode-range to break down a large font into smaller language-specific fonts, and use Monica Dinculescu’s font-style-matcher to minimize a jarring shift in layout, due to sizing discrepancies between the fallback and the web fonts.

Build Optimizations

  1. Set your priorities straight.
    It’s a good idea to know what you are dealing with first. Run an inventory of all of your assets (JavaScript, images, fonts, third-party scripts and “expensive” modules on the page, such as carousels, complex infographics and multimedia content), and break them down in groups.

    Set up a spreadsheet. Define the basic core experience for legacy browsers (i.e. fully accessible core content), the enhanced experience for capable browsers (i.e. the enriched, full experience) and the extras (assets that aren’t absolutely required and can be lazy-loaded, such as web fonts, unnecessary styles, carousel scripts, video players, social media buttons, large images). A while back, we published an article on “Improving Smashing Magazine’s Performance,” which describes this approach in detail.

    When optimizing for performance we need to reflect our priorities. Load the core experience immediately, then enhancements, and then the extras.

  2. Revisit the good ol’ “cutting-the-mustard” technique.
    These days we can still use the cutting-the-mustard technique to send the core experience to legacy browsers and an enhanced experience to modern browsers. An updated variant of the technique would use ES2015+ <script type="module">. Modern browsers would interpret the script as a JavaScript module and run it as expected, while legacy browsers wouldn’t recognize the attribute and ignore it because it’s unknown HTML syntax.

    These days we need to keep in mind that feature detection alone isn’t enough to make an informed decision about the payload to ship to that browser. On its own, cutting-the-mustard deduces device capability from browser version, which is no longer something we can do today.

    For example, cheap Android phones in developing countries mostly run Chrome and will cut the mustard despite their limited memory and CPU capabilities. Eventually, using the Device Memory Client Hints Header, we’ll be able to target low-end devices more reliably. At the moment of writing, the header is supported only in Blink (it goes for client hints in general). Since Device Memory also has a JavaScript API which is already available in Chrome, one option could be to feature detect based on the API, and fall back to “cutting the mustard” technique only if it’s not supported (thanks, Yoav!).

  3. Parsing JavaScript is expensive, so keep it small.
    When dealing with single-page applications, we need some time to initialize the app before we can render the page. Your setting will require your custom solution, but you could watch out for modules and techniques to speed up the initial rendering time. For example, here’s how to debug React performance and eliminate common React performance issues, and here’s how to improve performance in Angular. In general, most performance issues come from the initial parsing time to bootstrap the app.

    JavaScript has a cost, but it’s rarely the file size alone that drains on performance. Parsing and executing times vary significantly depending on the hardware of a device. On an average phone (Moto G4), a parsing time alone for 1MB of (uncompressed) JavaScript will be around 1.3–1.4s, with 15–20% of all time on mobile spent on parsing. With compiling in play, just prep work on JavaScript takes 4s on average, with around 11s before First Meaningful Paint on mobile. Reason: parse and execution times can easily be 2–5x times higher on low-end mobile devices.

    To guarantee high performance, as developers, we need to find ways to write and deploy less JavaScript. That’s why it pays off to examine every single JavaScript dependency in detail.

    There are many tools to help you make an informed decision about the impact of your dependencies and viable alternatives:

    An interesting way of avoiding parsing costs is to use binary templates that Ember has introduced in 2017. With them, Ember replaces JavaScript parsing with JSON parsing, which is presumably faster. (Thanks, Leonardo, Yoav!)

    Measure JavaScript parse and compile times. We can use synthetic testing tools and browser traces to track parse times, and browser implementors are talking about exposing RUM-based processing times in the future. Alternatively, consider using Etsy’s DeviceTiming, a little tool allowing you to instruct your JavaScript to measure parse and execution time on any device or browser.

    Bottom line: while size matters, it isn’t everything. Parse and compiling times don’t necessarily increase linearly when the script size increases.

  4. Are you using tree-shaking, scope hoisting and code-splitting?
    Tree-shaking is a way to clean up your build process by only including code that is actually used in production and eliminate unused imports in Webpack. With Webpack and Rollup, we also have scope hoisting that allows both tools to detect where import chaining can be flattened and converted into one inlined function without compromising the code. With Webpack, we can also use JSON Tree Shaking as well.

    Also, you might want to consider learning how to write efficient CSS selectors as well as how to avoid bloat and expensive styles. Feeling like going beyond that? You can also use Webpack to shorten the class names and use scope isolation
    to rename CSS class names dynamically at the compilation time.

    Code-splitting is another Webpack feature that splits your code base into “chunks” that are loaded on demand. Not all of the JavaScript has to be downloaded, parsed and compiled right away. Once you define split points in your code, Webpack can take care of the dependencies and outputted files. It enables you to keep the initial download small and to request code on demand when requested by the application. Alexander Kondrov has a fantastic introduction to code-splitting with Webpack and React.

    Consider using preload-webpack-plugin that takes routes you code-split and then prompts browser to preload them using <link rel="preload"> or <link rel="prefetch">. Webpack inline directives also give some control over preload/prefetch

    Where to define split points? By tracking which chunks of CSS/JavaScript are used, and which aren’t used. Umar Hansa explains how you can use Code Coverage from Devtools to achieve it.

    If you aren’t using Webpack, note that Rollup shows significantly better results than Browserify exports. While we’re at it, you might want to check out rollup-plugin-closure-compiler and Rollupify, which converts ECMAScript 2015 modules into one big CommonJS module — because small modules can have a surprisingly high performance cost depending on your choice of bundler and module system.

  5. Can you offload JavaScript into a Web Worker?
    To reduce the negative impact to Time-to-Interactive, it might be a good idea to look into offloading heavy JavaScript into a Web Worker or caching via a Service Worker.

    As the code base keeps growing, the UI performance bottlenecks will show up, slowing down the user’s experience. That’s because DOM operations are running alongside your JavaScript on the main thread. With web workers, we can move these expensive operations to a background process that’s running on a different thread. Typical use cases for web workers are prefetching data and Progressive Web Apps to load and store some data in advance so that you can use it later when needed. And you could use Comlink to streamline the communication between the main page and the worker. Still some work to do, but we are getting there.

    Workerize allows you to move a module into a Web Worker, automatically reflecting exported functions as asynchronous proxies. And if you’re using Webpack, you could use workerize-loader. Alternatively, you could use worker-plugin as well.

    Note that Web Workers don’t have access to the DOM because the DOM is not “thread-safe”, and the code that they execute needs to be contained in a separate file.

  6. Can you offload JavaScript into WebAssembly?
    We could potentialy also convert JavaScript into WebAssembly, a binary instruction format, designed as a portable target for compilation of high-level languages like C/C++/Rust. Its browser support is remarkable, and it has recently become viable as function calls between JavaSript and WASM are getting faster, at least in Firefox.

    In real-world scenarios, JavaScript seems to perform better than WebAssembly on smaller array sizes and WebAssembly performs better than JavaScript on larger array sizes. For most web apps, JavaScript is a better fit, and WebAssembly is best used for computationally intensive web apps, such as web games. However, it might be worth investigating if a switch to WebAssembly would result in noticeable performance improvements.

    If you’d like to learn more about WebAssembly:


A general overview of how WebAssembly works and why it’s useful.
Milica Mihajlija provides a general overview of how WebAssembly works and why it’s useful. (Large preview)
  1. Are you using an ahead-of-time compiler?
    Use an ahead-of-time compiler to offload some of the client-side rendering to the server and, hence, output usable results quickly. Finally, consider using Optimize.js for faster initial loading by wrapping eagerly invoked functions (it might not be necessary any longer, though).

'Fast By Default: Modern Loading Best Practices' by Addy Osmani

From Fast By Default: Modern Loading Best Practices by the one-and-only Addy Osmani. Slide 76.
  1. Serve legacy code only to legacy browsers.
    With ES2015 being remarkably well supported in modern browsers, we can use babel-preset-env to only transpile ES2015+ features unsupported by the modern browsers you are targeting. Then set up two builds, one in ES6 and one in ES5. As mentioned above, JavaScript modules are now supported in all major browsers, so use use script type="module" to let browsers with ES module support load the file, while older browsers could load legacy builds with script nomodule. And we can automate the entire process with Webpack ESNext Boilerplate.

    Note that these days we can write module-based JavaScript that runs natively in the browser, without transpilers or bundlers. <link rel="modulepreload"> header provides a way to initiate early (and high-priority) loading of module scripts. Basically, it’s a nifty way to help in maximizing bandwidth usage, by telling the browser about what it needs to fetch so that it’s not stuck with anything to do during those long roundtrips. Also, Jake Archibald has published a detailed article with gotchas and things t keep in mind with ES Modules that’s worth reading.

    For lodash, use babel-plugin-lodash that will load only modules that you are using in your source. Your dependencies might also depend on other versions of Lodash, so transform generic lodash requires to cherry-picked ones to avoid code duplication. This might save you quite a bit of JavaScript payload.

    Shubham Kanodia has written a detailed low-maintenance guide on smart bundling: to shipping legacy code to only legacy browsers in production with the code snippet you could use right away.


As explained in Jake Archibald’s article, inline scripts are deferred until blocking external scripts and inline scripts are executed.
Jake Archibald has published a detailed article with gotchas and things to keep in mind with ES Modules, e.g. inline scripts are deferred until blocking external scripts and inline scripts are executed. (Large preview)
  1. Are you using differential serving for JavaScript?
    We want to send just the necessary JavaScript through the network, yet it means being slightly more focused and granular about the delivery of those assets. A while back Philip Walton introduced the idea of differential serving. The idea is to compile and serve two separate JavaScript bundles: the “regular” build, the one with Babel-transforms and polyfills and serve them only to legacy browsers that actually need them, and another bundle (same functionality) that has no transforms or polyfills.

    As a result, we help reduce blocking of the main thread by reducing the amount of scripts the browser needs to process. Jeremy Wagner has published a comprehensive article on differential serving and how to set it up in your build pipeline in 2019, from setting up Babel, to what tweaks you’ll need to make in Webpack, as well as the benefits of doing all this work.

  2. Identify and rewrite legacy code with incremental decoupling.
    Long-living projects have a tendency to gather dust and dated code. Revisit your dependencies and assess how much time would be required to refactor or rewrite legacy code that has been causing trouble lately. Of course, it’s always a big undertaking, but once you know the impact of the legacy code, you could start with incremental decoupling.

    First, set up metrics that tracks if the ratio of legacy code calls is staying constant or going down, not up. Publicly discourage the team from using the library and make sure that your CI alerts developers if it’s used in pull requests. polyfills could help transition from legacy code to rewritten codebase that uses standard browser features.

  3. Identify and remove unused CSS/JS.
    CSS and JavaScript code coverage in Chrome allows you to learn which code has been executed/applied and which hasn’t. You can start recording the coverage, perform actions on a page, and then explore the code coverage results. Once you’ve detected unused code, find those modules and lazy load with import() (see the entire thread). Then repeat the coverage profile and validate that it’s now shipping less code on initial load.

    You can use Puppeteer to programmatically collect code coverage and Canary already allows you to export code coverage results, too. As Andy Davies noted, you might want to collect code coverage for both modern and legacy browsers though. There are many other use-cases for Puppeteer, such as, for example, automatic visual diffing or monitoring unused CSS with every build.

    Furthermore, purgecss, UnCSS and Helium can help you remove unused styles from CSS. And if you aren’t certain if a suspicious piece of code is used somewhere, you can follow Harry Roberts’ advice: create a 1×1px transparent GIF for a particular class and drop it into a dead/ directory, e.g. /assets/img/dead/comments.gif. After that, you set that specific image as a background on the corresponding selector in your CSS, sit back and wait for a few months if the file is going to appear in your logs. If there are no entries, nobody had that legacy component rendered on their screen: you can probably go ahead and delete it all.

    For the I-feel-adventurous-department, you could even automate gathering on unused CSS through a set of pages by monitoring DevTools using DevTools.

  4. Trim the size of your JavaScript bundles.
    As Addy Osmani noted, there’s a high chance you’re shipping full JavaScript libraries when you only need a fraction, along with dated polyfills for browsers that don’t need them, or just duplicate code. To avoid the overhead, consider using webpack-libs-optimizations that removes unused methods and polyfills during the build process.

    Add bundle auditing into your regular workflow as well. There might be some lightweight alternatives to heavy libraries you’ve added years ago, e.g. Moment.js could be replaced with date-fns or Luxon. Benedikt Rötsch’s research showed that a switch from Moment.js to date-fns could shave around 300ms for First paint on 3G and a low-end mobile phone.

    That’s where tools like Bundlephobia could help find the cost of adding a npm package to your bundle. You can even integrate these costs with a Lighthouse Custom Audit. This goes for frameworks, too. By removing or trimming the Vue MDC Adapter (Material Components for Vue), styles drop from 194KB to 10KB.

    Feeling adventurous? You could look into Prepack. It compiles JavaScript to equivalent JavaScript code, but unlike Babel or Uglify, it lets you write normal JavaScript code, and outputs equivalent JavaScript code that runs faster.

    Alternatively to shipping the entire framework, you could even trim your framework and compile it into a raw JavaScript bundle that does not require additional code. Svelte does it, and so does Rawact Babel plugin which transpiles React.js components to native DOM operations at build-time. Why? Well, as maintainers explain, “react-dom includes code for every possible component/HTMLElement that can be rendered, including code for incremental rendering, scheduling, event handling, etc. But there are applications which do not need all these features (at initial page load). For such applications, it might make sense to use native DOM operations to build the interactive user interface.”


Webpack comparison
In his article, Benedikt Rötsch’s showed that a switch from Moment.js to date-fns could shave around 300ms for First paint on 3G and a low-end mobile phone. (Large preview)
  1. Are you using predictive prefetching for JavaScript chunks?
    We could use heuristics to decide when to preload JavaScript chunks. Guess.js is a set of tools and libraries that use Google Analytics data to determine which page a user is mostly likely to visit next from a given page. Based on user navigation patterns collected from Google Analytics or other sources, Guess.js builds a machine-learning model to predict and prefetch JavaScript that will be required in each subsequent page.

    Hence, every interactive element is receiving a probability score for engagement, and based on that score, a client-side script decides to prefetch a resource ahead of time. You can integrate the technique to your Next.js application, Angular and React, and there is a Webpack plugin which automates the setup process as well.

    Obviously, you might be prompting the browser to consume unneeded data and prefetch undesirable pages, so it’s a good idea to be quite conservative in the number of prefetched requests. A good use case would be prefetching validation scripts required in the checkout, or speculative prefetch when a critical call-to-action comes into the viewport.

    Need something less sophisticated? Quicklink is a small library that automatically prefetches links in the viewport during idle time in attempt to make next-page navigations load faster. However, it’s also data-considerate, so it doesn’t prefetch on 2G or if Data-Saver is on.

  2. Take advantage of optimizations for your target JavaScript engine.
    Study what JavaScript engines dominate in your user base, then explore ways of optimizing for them. For example, when optimizing for V8 which is used in Blink-browsers, Node.js runtime and Electron, make use of script streaming for monolithic scripts. It allows async or defer scripts to be parsed on a separate background thread once downloading begins, hence in some cases improving page loading times by up to 10%. Practically, use <script defer> in the <head>, so that the browsers can discover the resource early and then parse it on the background thread.

    Caveat: Opera Mini doesn’t support script deferment, so if you are developing for India or Africa, defer will be ignored, resulting in blocking rendering until the script has been evaluated (thanks Jeremy!).


Progressive booting
Progressive booting means using server-side rendering to get a quick first meaningful paint, but also include some minimal JavaScript to keep the time-to-interactive close to the first meaningful paint.
  1. Client-side rendering or server-side rendering?
    In both scenarios, our goal should be to set up progressive booting: Use server-side rendering to get a quick first meaningful paint, but also include some minimal necessary JavaScript to keep the time-to-interactive close to the first meaningful paint. If JavaScript is coming too late after the First Meaningful Paint, the browser might lock up the main thread while parsing, compiling and executing late-discovered JavaScript, hence handcuffing the interactivity of site or application.

    To avoid it, always break up the execution of functions into separate, asynchronous tasks, and where possible use requestIdleCallback. Consider lazy loading parts of the UI using WebPack’s dynamic import() support, avoiding the load, parse, and compile cost until the users really need them (thanks Addy!).

    In its essence, Time to Interactive (TTI) tells us the time between navigation and interactivity. The metric is defined by looking at the first five-second window after the initial content is rendered, in which no JavaScript tasks take longer than 50ms. If a task over 50ms occurs, the search for a five-second window starts over. As a result, the browser will first assume that it reached Interactive, just to switch to Frozen, just to eventually switch back to Interactive.

    Once we reached Interactive, we can then — either on demand or as time allows — boot non-essential parts of the app. Unfortunately, as Paul Lewis noticed, frameworks typically have no concept of priority that can be surfaced to developers, and hence progressive booting is difficult to implement with most libraries and frameworks. If you have the time and resources, use this strategy to ultimately boost performance.

    So, client-side or server-side? If there is no visible benefit to the user, client-side rendering might not be really necessary — actually, server-side-rendered HTML could be faster. Perhaps you could even pre-render some of your content with static site generators and push them straight to the CDNs, with some JavaScript on top.

    Limit the use of client-side frameworks to pages that absolutely require them. Server-rendering and client-rendering are a disaster if done poorly. Consider pre-rendering at build time and CSS inlining on the fly to produce production-ready static files. Addy Osmani has given a fantastic talk on the Cost of JavaScript that might be worth watching.

  2. Constrain the impact of third-party scripts.
    With all performance optimizations in place, often we can’t control third-party scripts coming from business requirements. Third-party-scripts metrics aren’t influenced by end-user experience, so too often one single script ends up calling a long tail of obnoxious third-party scripts, hence ruining a dedicated performance effort. To contain and mitigate performance penalties that these scripts bring along, it’s not enough to just load them asynchronously (probably via defer) and accelerate them via resource hints such as dns-prefetch or preconnect.

    As Yoav Weiss explained in his must-watch talk on third-party scripts, in many cases these scripts download resources that are dynamic. The resources change between page loads, so we don’t necessarily know which hosts the resources will be downloaded from and what resources they would be.

    What options do we have then? Consider using service workers by racing the resource download with a timeout and if the resource hasn’t responded within a certain timeout, return an empty response to tell the browser to carry on with parsing of the page. You can also log or block third-party requests that aren’t successful or don’t fulfill certain criteria. If you can, load the 3rd-party-script from your own server rather than from the vendor’s server.


    Casper.com published a detailed case study on how they managed to shave 1.7 seconds off the site by self-hosting Optimizely. It might be worth it.
    Casper.com published a detailed case study on how they managed to shave 1.7 seconds off the site by self-hosting Optimizely. It might be worth it. (Image source) (Large preview)

    Another option is to establish a Content Security Policy (CSP) to restrict the impact of third-party scripts, e.g. disallowing the download of audio or video. The best option is to embed scripts via <iframe> so that the scripts are running in the context of the iframe and hence don’t have access to the DOM of the page, and can’t run arbitrary code on your domain. Iframes can be further constrained using the sandbox attribute, so you can disable any functionality that iframe may do, e.g. prevent scripts from running, prevent alerts, form submission, plugins, access to the top navigation, and so on.

    For example, it’s probably going to be necessary to allow scripts to run with <iframe sandbox="allow-scripts">. Each of the limitations can be lifted via various allow values on the sandbox attribute (supported almost everywhere), so constrain them to the bare minimum of what they should be allowed to do.

    Consider using Intersection Observer; that would enable ads to be iframed while still dispatching events or getting the information that they need from the DOM (e.g. ad visibility). Watch out for new policies such as Feature policy, resource size limits and CPU/Bandwidth priority to limit harmful web features and scripts that would slow down the browser, e.g. synchronous scripts, synchronous XHR requests, document.write and outdated implementations.

    To stress-test third parties, examine bottom-up summaries in Performance profile page in DevTools, test what happens if a request is blocked or it has timed out — for the latter, you can use WebPageTest’s Blackhole server blackhole.webpagetest.org that you can point specific domains to in your hosts file. Preferably self-host and use a single hostname, but also generate a request map that exposes fourth-party calls and detect when the scripts change. You can use Harry Roberts’ approach for auditing third parties and produce spreadsheets like this one. Harry also explains the auditing workflow in his talk on third-party performance and auditing.

request blocking

Image credit: Harry Roberts
  1. Set HTTP cache headers properly.
    Double-check that expires, max-age, cache-control, and other HTTP cache headers have been set properly. In general, resources should be cacheable either for a very short time (if they are likely to change) or indefinitely (if they are static) — you can just change their version in the URL when needed. Disable the Last-Modified header as any asset with it will result in a conditional request with an If-Modified-Since-header even if the resource is in cache. Same with Etag.

    Use Cache-control: immutable, designed for fingerprinted static resources, to avoid revalidation (as of December 2018, supported in Firefox, Edge and Safari; in Firefox only on https:// transactions). In fact “across all of the pages in the HTTP Archive, 2% of requests and 30% of sites appear to include at least 1 immutable response. Additionally, most of the sites that are using it have the directive set on assets that have a long freshness lifetime.”

    Remember the stale-while-revalidate? As you probably know, we specify the caching time with the Cache-Control response header, e.g. Cache-Control: max-age=604800. After 604800 seconds have passed, the cache will re-fetch the requested content, causing the page to load slower. This slowdown can be avoided by using stale-while-revalidate; it basically defines an extra window of time during which a cache can use a stale asset as long as it revalidates it async in the background. Thus, it “hides” latency (both in the network and on the server) from clients.

    In October 2018, Chrome published an intent to ship handling of stale-while-revalidate in HTTP Cache-Control header, so as a result, it should improve subsequent page load latencies as stale assets are no longer in the critical path. Result: zero RTT for repeat views.

    You can use Heroku’s primer on HTTP caching headers, Jake Archibald’s “Caching Best Practices” and Ilya Grigorik’s HTTP caching primer as guides. Also, be wary of the vary header, especially in relation to CDNs, and watch out for the Key header which helps avoiding an additional round trip for validation whenever a new request differs slightly (but not significantly) from prior requests (thanks, Guy!).

    Also, double-check that you aren’t sending unnecessary headers (e.g. x-powered-by, pragma, x-ua-compatible, expires and others) and that you include useful security and performance headers (such as Content-Security-Policy, X-XSS-Protection, X-Content-Type-Options and others). Finally, keep in mind the performance cost of CORS requests in single-page applications.

Delivery Optimizations

  1. Do you load all JavaScript libraries asynchronously?
    When the user requests a page, the browser fetches the HTML and constructs the DOM, then fetches the CSS and constructs the CSSOM, and then generates a rendering tree by matching the DOM and CSSOM. If any JavaScript needs to be resolved, the browser won’t start rendering the page until it’s resolved, thus delaying rendering. As developers, we have to explicitly tell the browser not to wait and to start rendering the page. The way to do this for scripts is with the defer and async attributes in HTML.

    In practice, it turns out we should prefer defer to async (at a cost to users of Internet Explorer up to and including version 9, because you’re likely to break scripts for them). According to Steve Souders, once async scripts arrive, they are executed immediately. If that happens very fast, for example when the script is in cache aleady, it can actually block HTML parser. With defer, browser doesn’t execute scripts until HTML is parsed. So, unless you need JavaScript to execute before start render, it’s better to use defer.

    Also, as mentioned above, limit the impact of third-party libraries and scripts, especially with social sharing buttons and <iframe> embeds (such as maps). Size Limit helps you prevent JavaScript libraries bloat: If you accidentally add a large dependency, the tool will inform you and throw an error. You can use static social sharing buttons (such as by SSBG) and static links to interactive maps instead.

    You might want to revise your non-blocking script loader for CSP compliance.

  2. Lazy load expensive components with IntersectionObserver.
    In general, it’s a good idea to lazy-load all expensive components, such as heavy JavaScript, videos, iframes, widgets, and potentially images. The most performant way to do so is by using the Intersection Observer API that provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document’s viewport. Basically, you need to create a new IntersectionObserver object, which receives a callback function and a set of options. Then we add a target to observe.

    The callback function executes when the target becomes visible or invisible, so when it intercepts the viewport, you can start taking some actions before the element becomes visible. In fact, we have a granular control over when the observer’s callback should be invoked, with rootMargin (margin around the root) and threshold (a single number or an array of numbers which indicate at what percentage of the target’s visibility we are aiming).

    Alejandro Garcia Anglada has published a handy tutorial on how to actually implement it, Rahul Nanwani wrote a detailed post on lazy-loading foreground and background images, and Google Fundamentals provide a detailed tutorial on lazy loading images and video with Intersection Observer as well. Remember art-directed storytelling long reads with moving and sticky objects? You can implement performant scrollytelling with Intersection Observer, too.

    Also, watch out for the lazyload attribute that will allow us to specify which images and iframes should be lazy loaded, natively. Feature policy: LazyLoad will provide a mechanism that allows us to force opting in or out of LazyLoad functionality on a per-domain basis (similar to how Content Security Policies work). Bonus: once shipped, priority hints will allow us to specify importance on scripts and preloads in the header as well (currently in Chrome Canary).

  3. Load images progressively.
    You could even take lazy loading to the next level by adding progressive image loading to your pages. Similarly to Facebook, Pinterest and Medium, you could load low quality or even blurry images first, and then as the page continues to load, replace them with the full quality versions by using the LQIP (Low Quality Image Placeholders) technique proposed by Guy Podjarny.

    Opinions differ if these techniques improve user experience or not, but it definitely improves time to first meaningful paint. We can even automate it by using SQIP that creates a low quality version of an image as an SVG placeholder, or Gradient Image Placeholders with CSS linear gradients. These placeholders could be embedded within HTML as they naturally compress well with text compression methods. In his article, Dean Hume has described how this technique can be implemented using Intersection Observer.

    Browser support? Decent, with Chrome, Firefox, Edge and Samsung Internet being on board. WebKit status is currently supported in preview. Fallback? If the browser doesn’t support intersection observer, we can still lazy load a polyfill or load the images immediately. And there is even a library for it.

    Want to go fancier? You could trace your images and use primitive shapes and edges to create a lightweight SVG placeholder, load it first, and then transition from the placeholder vector image to the (loaded) bitmap image.


  4. SVG lazy loading technique by José M. Pérez
    SVG lazy loading technique by José M. Pérez. (Large preview)
  5. Do you send critical CSS?
    To ensure that browsers start rendering your page as quickly as possible, it’s become a common practice to collect all of the CSS required to start rendering the first visible portion of the page (known as “critical CSS” or “above-the-fold CSS”) and add it inline in the <head> of the page, thus reducing roundtrips. Due to the limited size of packages exchanged during the slow start phase, your budget for critical CSS is around 14 KB.

    If you go beyond that, the browser will need additional roundtrips to fetch more styles. CriticalCSS and Critical enable you to do just that. You might need to do it for every template you’re using. If possible, consider using the conditional inlining approach used by the Filament Group, or convert inline code to static assets on the fly.

    With HTTP/2, critical CSS could be stored in a separate CSS file and delivered via a server push without bloating the HTML. The catch is that server pushing is troublesome with many gotchas and race conditions across browsers. It isn’t supported consistently and has some caching issues (see slide 114 onwards of Hooman Beheshti’s presentation). The effect could, in fact, be negative and bloat the network buffers, preventing genuine frames in the document from being delivered. Also, it appears that server pushing is much more effective on warm connections due to the TCP slow start.

    Even with HTTP/1, putting critical CSS in a separate file on the root domain has benefits, sometimes even more than inlining due to caching. Chrome speculatively opens a second HTTP connection to the root domain when requesting the page, which removes the need for a TCP connection to fetch this CSS (thanks, Philip!)

    A few gotchas to keep in mind: unlike preload that can trigger preload from any domain, you can only push resources from your own domain or domains you are authoritative for. It can be initiated as soon as the server gets the very first request from the client. Server pushed resources land in the Push cache and are removed when the connection is terminated. However, since an HTTP/2 connection can be re-used across multiple tabs, pushed resources can be claimed by requests from other tabs as well (thanks, Inian!).

    At the moment, there is no simple way for the server to know if pushed resources are already in one of the user’s caches, so resources will keep being pushed with every user’s visit. You may then need to create a cache-aware HTTP/2 server push mechanism. If fetched, you could try to get them from a cache based on the index of what’s already in the cache, avoiding secondary server pushes altogether.

    Keep in mind, though, that the new cache-digest specification negates the need to manually build such “cache-aware” servers, basically declaring a new frame type in HTTP/2 to communicate what’s already in the cache for that hostname. As such, it could be particularly useful for CDNs as well.

    For dynamic content, when a server needs some time to generate a response, the browser isn’t able to make any requests since it’s not aware of any sub-resources that the page might reference. For that case, we can warm up the connection and increase the TCP congestion window size, so that future requests can be completed faster. Also, all inlined assets are usually good candidates for server pushing. In fact, Inian Parameshwaran did remarkable research comparing HTTP/2 Push vs. HTTP Preload, and it’s a fantastic read with all the details you might need. Server Push or Not Server Push? Colin Bendell’s Should I Push? might point you in the right direction.

    Bottom line: As Sam Saccone noted, preload is good for moving the start download time of an asset closer to the initial request, while Server Push is good for cutting out a full RTT (or more, depending on your server think time) — if you have a service worker to prevent unnecessary pushing, that is.

  6. Experiment with regrouping your CSS rules.
    We’ve got used to critical CSS, but there are a few optimizations that could go beyond that. Harry Roberts conducted a remarkable research with quite surprising results. For example, it might be a good idea to split the main CSS file out into its individual media queries. That way, the browser will retrieve critical CSS with high priority, and everything else with low priority — completely off the critical path.

    Also, avoid placing <link rel="stylesheet" /> before async snippets. If scripts don’t depend on stylesheets, consider placing blocking scripts above blocking styles. If they do, split that JavaScript in two and load it either side of your CSS.

    Scott Jehl solved another interesting problem by caching an inlined CSS file with a service worker, a common problem familiar if you’re using critical CSS. Basically, we add an ID attribute onto the style element so that it’s easy to find it using JavaScript, then a small piece of JavaScript finds that CSS and uses the Cache API to store it in a local browser cache (with a content type of text/css) for use on subsequent pages. To avoid inlining on subsequent pages and instead reference the cached assets externally, we then set a cookie on the first visit to a site. Voilà!

Do we stream reponses? With streaming, HTML rendered during the initial navigation request can take full advantage of the browser’s streaming HTML parser.
  1. Do you stream responses?
    Often forgotten and neglected, streams provide an interface for reading or writing asynchronous chunks of data, only a subset of which might be available in memory at any given time. Basically, they allow the page that made the original request to start working with the response as soon as the first chunk of data is available, and use parsers that are optimized for streaming to progressively display the content.

    We could create one stream from multiple sources. For example, instead of serving an empty UI shell and letting JavaScript populate it, you can let the service worker construct a stream where the shell comes from a cache, but the body comes from the network. As Jeff Posnick noted, if your web app is powered by a CMS that server-renders HTML by stitching together partial templates, that model translates directly into using streaming responses, with the templating logic replicated in the service worker instead of your server. Jake Archibald’s The Year of Web Streams article highlights how exactly you could build it. Performance boost is quite noticeable.

    One important advantage of streaming the entire HTML response is that HTML rendered during the initial navigation request can take full advantage of the browser’s streaming HTML parser. Chunks of HTML that are inserted into a document after the page has loaded (as is common with content populated via JavaScript) can’t take advantage of this optimization.

    Browser support? Getting there with Chrome 52+, Firefox 57+ (behind flag), Safari and Edge supporting the API and Service Workers being supported in all modern browsers.

  2. Consider making your components connection-aware.
    Data can be expensive and with growing payload, we need to respect users who choose to opt into data savings while accessing our sites or apps. The Save-Data client hint request header allows us to customize the application and the payload to cost- and performance-constrained users. In fact, you could rewrite requests for high DPI images to low DPI images, remove web fonts, fancy parallax effects, preview thumbnails and infinite scroll, turn off video autoplay, server pushes, reduce the number of displayed items and downgrade image quality, or even change how you deliver markup. Tim Vereecke has published a very detailed article on data-s(h)aver strategies featuring many options for data saving.

    The header is currently supported only in Chromium, on the Android version of Chrome or via the Data Saver extension on a desktop device. Finally, you can also use the Network Information API to deliver low/high resolution images and videos based on the network type. Network Information API and specifically navigator.connection.effectiveType (Chrome 62+) use RTT, downlink, effectiveType values (and a few others) to provide a representation of the connection and the data that users can handle.

    In this context, Max Stoiber speaks of connection-aware components. For example, with React, we could write a component that renders different elements for different connection types. As Max suggested, a <Media /> component in a news article might output:

    • Offline: a placeholder with alt text,
    • 2G / save-data mode: a low-resolution image,
    • 3G on non-Retina screen: a mid-resolution image,
    • 3G on Retina screens: high-res Retina image,
    • 4G: an HD video.

    Dean Hume provides a practical implementation of a similar logic using a service worker. For a video, we could display a video poster by default, and then display the “Play” icon as well as the video player shell, meta-data of the video etc. on better connections. As a fallback for non-supporting browsers, we could listen to canplaythrough event and use Promise.race() to timeout the source loading if the canplaythrough event doesn’t fire within 2 seconds.

  3. Consider making your components device memory-aware.
    Network connection gives us only one perspective at the context of the user though. Going further, you could also dynamically adjust resources based on available device memory, with the Device Memory API (Chrome 63+). navigator.deviceMemory returns how much RAM the device has in gigabytes, rounded down to the nearest power of two. The API also features a Client Hints Header, Device-Memory, that reports the same value.

The priority column in DevTools

The ‘Priority’ column in DevTools. Image credit: Ben Schwarz, The Critical Request
  1. Warm up the connection to speed up delivery.
    Use resource hints to save time on dns-prefetch (which performs a DNS lookup in the background), preconnect (which asks the browser to start the connection handshake (DNS, TCP, TLS) in the background), prefetch (which asks the browser to request a resource) and preload (which prefetches resources without executing them, among other things).

    Most of the time these days, we’ll be using at least preconnect and dns-prefetch, and we’ll be cautious with using prefetch and preload; the former should only be used if you are confident about what assets the user will need next (for example, in a purchasing funnel).

    Note that even with preconnect and dns-prefetch, the browser has a limit on the number of hosts it will look up/connect to in parallel, so it’s a safe bet to order them based on priority (thanks Philip!).

    In fact, using resource hints is probably the easiest way to boost performance, and it works well indeed. When to use what? As Addy Osmani has explained, we should preload resources that we have high-confidence will be used in the current page. Prefetch resources likely to be used for future navigations across multiple navigation boundaries, e.g. Webpack bundles needed for pages the user hasn’t visited yet.

    Addy’s article on “Loading Priorities in Chrome” shows how exactly Chrome interprets resource hints, so once you’ve decided which assets are critical for rendering, you can assign high priority to them. To see how your requests are prioritized, you can enable a “priority” column in the Chrome DevTools network request table (as well as Safari Technology Preview).

    For example, since fonts usually are important assets on a page, it’s always a good idea to request the browser to download fonts with preload. You could also load JavaScript dynamically, effectively lazy-loading execution. Also, since <link rel="preload"> accepts a media attribute, you could choose to selectively prioritize resources based on @media query rules.

    A few gotchas to keep in mind: preload is good for moving the start download time of an asset closer to the initial request, but preloaded assets land in the memory cache which is tied to the page making the request. preload plays well with the HTTP cache: a network request is never sent if the item is already there in the HTTP cache.

    Hence, it’s useful for late-discovered resources, a hero image loaded via background-image, inlining critical CSS (or JavaScript) and pre-loading the rest of the CSS (or JavaScript). Also, a preload tag can initiate a preload only after the browser has received the HTML from the server and the lookahead parser has found the preload tag.

    Preloading via the HTTP header is a bit faster since we don’t to wait for the browser to parse the HTML to start the request. Early Hints will help even further, enabling preload to kick in even before the response headers for the HTML are sent and Priority Hints (coming soon) will help us indicate loading priorities for scripts.

    Beware: if you’re using preload, as must be defined or nothing loads, plus preloaded fonts without the crossorigin attribute will double fetch.

  2. Use service workers for caching and network fallbacks.
    No performance optimization over a network can be faster than a locally stored cache on a user’s machine. If your website is running over HTTPS, use the “Pragmatist’s Guide to Service Workers” to cache static assets in a service worker cache and store offline fallbacks (or even offline pages) and retrieve them from the user’s machine, rather than going to the network. Also, check Jake’s Offline Cookbook and the free Udacity course “Offline Web Applications.”

    Browser support? As stated above, it’s widely supported (Chrome, Firefox, Safari TP, Samsung Internet, Edge 17+) and the fallback is the network anyway. Does it help boost performance? Oh yes, it does. And it’s getting better, e.g. with Background Fetch allowing background uploads/downloads from a service worker. Shipped in Chrome 71.

    There are a number of use cases for a service worker. For example, you could implement “Save for offline” feature, handle broken images, introduce messaging between tabs or provide different caching strategies based on request types. In general, a common reliable strategy is to store the app shell in the service worker’s cache along with a few critical pages, such as offline page, frontpage and anything else that might be important in your case.

    There are a few gotchas to keep in mind though. With a service worker in place, we need to beware range requests in Safari (if you are using Workbox for a service worker it has a range request module). If you ever stumbled upon DOMException: Quota exceeded. error in the browser console, then look into Gerardo’s article When 7KB equals 7MB.

    As Gerardo writes, “If you are building a progressive web app and are experiencing bloated cache storage when your service worker caches static assets served from CDNs, make sure the proper CORS response header exists for cross-origin resources, you do not cache opaque responses with your service worker unintentionally, you opt-in cross-origin image assets into CORS mode by adding the crossorigin attribute to the <img> tag.”

    A good starting point for using service workers would be Workbox, a set of service worker libraries built specifically for building progressive web apps.

  3. Are you using service workers on the CDN/Edge, e.g. for A/B testing?
    At this point, we are quite used to running service workers on the client, but with CDNs implementing them on the server, we could use them to tweak performance on the edge as well.

    For example, in A/B tests, when HTML needs to vary its content for different users, we could use Service Workers on the CDN servers to handle the logic. We could also stream HTML rewriting to speed up sites that use Google Fonts.

  4. Optimize rendering performance.
    Isolate expensive components with CSS containment — for example, to limit the scope of the browser’s styles, of layout and paint work for off-canvas navigation, or of third-party widgets. Make sure that there is no lag when scrolling the page or when an element is animated, and that you’re consistently hitting 60 frames per second. If that’s not possible, then at least making the frames per second consistent is preferable to a mixed range of 60 to 15. Use CSS’ will-change to inform the browser of which elements and properties will change.

    Also, measure runtime rendering performance (for example, in DevTools). To get started, check Paul Lewis’ free Udacity course on browser-rendering optimization and Georgy Marchuk’s article on Browser painting and considerations for web performance.

    If you want to dive deeper into the topic, Nolan Lawson has shared tricks to accurately measure layout performance in his article, and Jason Miller suggested alternative techniques, too. We also have a lil’ article by Sergey Chikuyonok on how to get GPU animation right. Quick note: changes to GPU-composited layers are the least expensive, so if you can get away by triggering only compositing via opacity and transform, you’ll be on the right track. Anna Migas has provided a lot of practical advice in her talk on Debugging UI Rendering Performance, too.

  5. Have you optimized rendering experience?
    While the sequence of how components appear on the page, and the strategy of how we serve assets to the browser matter, we shouldn’t underestimate the role of perceived performance, too. The concept deals with psychological aspects of waiting, basically keeping customers busy or engaged while something else is happening. That’s where perception management, preemptive start, early completion and tolerance management come into play.

    What does it all mean? While loading assets, we can try to always be one step ahead of the customer, so the experience feels swift while there is quite a lot happening in the background. To keep the customer engaged, we can test skeleton screens (implementation demo) instead of loading indicators, add transitions/animations and basically cheat the UX when there is nothing more to optimize. Beware though: skeleton screens should be tested before deploying as some tests showed that skeleton screens can perform the worst by all metrics.

HTTP/2

  1. Migrate to HTTPS, then turn on HTTP/2.
    With Google moving towards a more secure web and eventual treatment of all HTTP pages in Chrome as being “not secure,” a switch to HTTP/2 environment is unavoidable. HTTP/2 is supported very well; it isn’t going anywhere; and, in most cases, you’re better off with it. Once running on HTTPS already, you can get a major performance boost with service workers and server push (at least long term).

    HTTP/2

    Eventually, Google plans to label all HTTP pages as non-secure, and change the HTTP security indicator to the red triangle that Chrome uses for broken HTTPS. (Image source)

    The most time-consuming task will be to migrate to HTTPS, and depending on how large your HTTP/1.1 user base is (that is, users on legacy operating systems or with legacy browsers), you’ll have to send a different build for legacy browsers performance optimizations, which would require you to adapt to a different build process. Beware: Setting up both migration and a new build process might be tricky and time-consuming. For the rest of this article, I’ll assume that you’re either switching to or have already switched to HTTP/2.

  2. Properly deploy HTTP/2.
    Again, serving assets over HTTP/2 requires a partial overhaul of how you’ve been serving assets so far. You’ll need to find a fine balance between packaging modules and loading many small modules in parallel. At the end of the day, still the best request is no request, however, the goal is to find a fine balance between quick first delivery of assets and caching.

    On the one hand, you might want to avoid concatenating assets altogether, instead breaking down your entire interface into many small modules, compressing them as a part of the build process, referencing them via the “scout” approach and loading them in parallel. A change in one file won’t require the entire style sheet or JavaScript to be re-downloaded. It also minimizes parsing time and keeps the payloads of individual pages low.

    On the other hand, packaging still matters. First, compression will suffer. The compression of a large package will benefit from dictionary reuse, whereas small separate packages will not. There’s standard work to address that, but it’s far out for now. Secondly, browsers have not yet been optimized for such workflows. For example, Chrome will trigger inter-process communications (IPCs) linear to the number of resources, so including hundreds of resources will have browser runtime costs.

    Progressive CSS loading

    To achieve best results with HTTP/2, consider to load CSS progressively, as suggested by Chrome’s Jake Archibald.

    Still, you can try to load CSS progressively. In fact, since Chrome 69, in-body CSS no longer blocks rendering for Chrome. Obviously, by doing so, you are actively penalizing HTTP/1.1 users, so you might need to generate and serve different builds to different browsers as part of your deployment process, which is where things get slightly more complicated. You could get away with HTTP/2 connection coalescing, which allows you to use domain sharding while benefiting from HTTP/2, but achieving this in practice is difficult, and in general, it’s not considered to be good practice.

    What to do? Well, if you’re running over HTTP/2, sending around 6–10 packages seems like a decent compromise (and isn’t too bad for legacy browsers). Experiment and measure to find the right balance for your website.

  3. Do your servers and CDNs support HTTP/2?
    Different servers and CDNs are probably going to support HTTP/2 differently. Use Is TLS Fast Yet? to check your options, or quickly look up how your servers are performing and which features you can expect to be supported.

    Consult Pat Meenan’s incredible research on HTTP/2 priorities and test server support for HTTP/2 prioritization. According to Pat, it’s recommended to enable BBR congestion control and set tcp_notsent_lowat to 16KB for HTTP/2 prioritization to work reliably on Linux 4.9 kernels and later (thanks, Yoav!). Andy Davies did a similar research for HTTP/2 prioritization across browsers, CDNs and Cloud Hosting Services.

Is TLS Fast Yet?

Is TLS Fast Yet? allows you to check your options for servers and CDNs when switching to HTTP/2. (Large preview)
  1. Is OCSP stapling enabled?
    By enabling OCSP stapling on your server, you can speed up your TLS handshakes. The Online Certificate Status Protocol (OCSP) was created as an alternative to the Certificate Revocation List (CRL) protocol. Both protocols are used to check whether an SSL certificate has been revoked. However, the OCSP protocol does not require the browser to spend time downloading and then searching a list for certificate information, hence reducing the time required for a handshake.
  2. Have you adopted IPv6 yet?
    Because we’re running out of space with IPv4 and major mobile networks are adopting IPv6 rapidly (the US has reached a 50% IPv6 adoption threshold), it’s a good idea to update your DNS to IPv6 to stay bulletproof for the future. Just make sure that dual-stack support is provided across the network — it allows IPv6 and IPv4 to run simultaneously alongside each other. After all, IPv6 is not backwards-compatible. Also, studies show that IPv6 made those websites 10 to 15% faster due to neighbor discovery (NDP) and route optimization.
  3. Is HPACK compression in use?
    If you’re using HTTP/2, double-check that your servers implement HPACK compression for HTTP response headers to reduce unnecessary overhead. Because HTTP/2 servers are relatively new, they may not fully support the specification, with HPACK being an example. H2spec is a great (if very technically detailed) tool to check that. HPACK’s compression algorithm is quite impressive, and it works.
  4. Make sure the security on your server is bulletproof.
    All browser implementations of HTTP/2 run over TLS, so you will probably want to avoid security warnings or some elements on your page not working. Double-check that your security headers are set properly, eliminate known vulnerabilities, and check your certificate. Also, make sure that all external plugins and tracking scripts are loaded via HTTPS, that cross-site scripting isn’t possible and that both HTTP Strict Transport Security headers and Content Security Policy headers are properly set.

Testing And Monitoring

  1. Have you optimized your auditing workflow?
    It might not sound like a big deal, but having the right settings in place at your fingertips might save you quite a bit of time in testing. Consider using Tim Kadlec’s Alfred Workflow for WebPageTest for submitting a test to the public instance of WebPageTest.

    You could also drive WebPageTest from a Google Spreadsheet and incorporate accessibility, performance and SEO scores into your Travis setup with Lighthouse CI or straight into Webpack.

    And if you need to debug something quickly but your build process seems to be remarkably slow, keep in mind that “whitespace removal and symbol mangling accounts for 95% of the size reduction in minified code for most JavaScript — not elaborate code transforms. You can simply disable compression to speed up Uglify builds by 3 to 4 times.”


pull request checks review required
Integrating accessibility, performance and SEO scores into your Travis setup with Lighthouse CI will highlight the performance impact of a new feature to all contributing developers. (Image source) (Large preview)
  1. Have you tested in proxy browsers and legacy browsers?
    Testing in Chrome and Firefox is not enough. Look into how your website works in proxy browsers and legacy browsers. UC Browser and Opera Mini, for instance, have a significant market share in Asia (up to 35% in Asia). Measure average Internet speed in your countries of interest to avoid big surprises down the road. Test with network throttling, and emulate a high-DPI device. BrowserStack is fantastic, but test on real devices as well.
k6 allows you write unit tests-alike performance tests.
  1. Have you tested the accessibility performance?
    When the browser starts to load a page, it builds a DOM, and if there is an assistive technology like a screen reader running, it also creates an accessibility tree. The screen reader then has to query the accessibility tree to retrieve the information and make it available to the user — sometimes by default, and sometimes on demand. And sometimes it takes time.

    When talking about fast Time to Interactive, usually we mean an indicator of how soon a user can interact with the page by clicking or tapping on links and buttons. The context is slightly different with screen readers. In that case, fast Time to Interactive means how much time passes by until the screen reader can announce navigation on a given page and a screen reader user can actually hit keyboard to interact.

    Léonie Watson has given an eye-opening talk on accessibility performance and specifically the impact slow loading has on screen reader announcement delays. Screen readers are used to fast-paced announcements and quick navigation, and therefore might potentially be even less patient than sighted users.

    Large pages and DOM manipulations with JavaScript will cause delays in screen reader announcements. A rather unexplored area that could use some attention and testing as screen readers are available on literally every platform (Jaws, NVDA, Voiceover, Narrator, Orca).

  2. Is continuous monitoring set up?
    Having a private instance of WebPagetest is always beneficial for quick and unlimited tests. However, a continuous monitoring tool — like Sitespeed, Calibre and SpeedCurve — with automatic alerts will give you a more detailed picture of your performance. Set your own user-timing marks to measure and monitor business-specific metrics. Also, consider adding automated performance regression alerts to monitor changes over time.

    Look into using RUM-solutions to monitor changes in performance over time. For automated unit-test-alike load testing tools, you can use k6 with its scripting API. Also, look into SpeedTracker, Lighthouse and Calibre.

Quick Wins

This list is quite comprehensive, and completing all of the optimizations might take quite a while. So, if you had just 1 hour to get significant improvements, what would you do? Let’s boil it all down to 12 low-hanging fruits. Obviously, before you start and once you finish, measure results, including start rendering time and Speed Index on a 3G and cable connection.

  1. Measure the real world experience and set appropriate goals. A good goal to aim for is First Meaningful Paint < 1 s, a Speed Index value < 1250, Time to Interactive < 5s on slow 3G, for repeat visits, TTI < 2s. Optimize for start rendering time and time-to-interactive.
  2. Prepare critical CSS for your main templates, and include it in the <head> of the page. (Your budget is 14 KB). For CSS/JS, operate within a critical file size budget of max. 170KB gzipped (0.7MB decompressed).
  3. Trim, optimize, defer and lazy-load as many scripts as possible, check lightweight alternatives and limit the impact of third-party scripts.
  4. Serve legacy code only to legacy browsers with <script type="module">.
  5. Experiment with regrouping your CSS rules and test in-body CSS.
  6. Add resource hints to speed up delivery with faster dns-lookup, preconnect, prefetch and preload.
  7. Subset web fonts and load them asynchronously, and utilize font-display in CSS for fast first rendering.
  8. Optimize images, and consider using WebP for critical pages (such as landing pages).
  9. Check that HTTP cache headers and security headers are set properly.
  10. Enable Brotli or Zopfli compression on the server. (If that’s not possible, don’t forget to enable Gzip compression.)
  11. If HTTP/2 is available, enable HPACK compression and start monitoring mixed-content warnings. Enable OCSP stapling.
  12. Cache assets such as fonts, styles, JavaScript and images in a service worker cache.

Download The Checklist (PDF, Apple Pages)

With this checklist in mind, you should be prepared for any kind of front-end performance project. Feel free to download the print-ready PDF of the checklist as well as an editable Apple Pages document to customize the checklist for your needs:

If you need alternatives, you can also check the front-end checklist by Dan Rublic, the “Designer’s Web Performance Checklist” by Jon Yablonski and the FrontendChecklist.

Off We Go!

Some of the optimizations might be beyond the scope of your work or budget or might just be overkill given the legacy code you have to deal with. That’s fine! Use this checklist as a general (and hopefully comprehensive) guide, and create your own list of issues that apply to your context. But most importantly, test and measure your own projects to identify issues before optimizing. Happy performance results in 2019, everyone!


A huge thanks to Guy Podjarny, Yoav Weiss, Addy Osmani, Artem Denysov, Denys Mishunov, Ilya Pukhalski, Jeremy Wagner, Colin Bendell, Mark Zeman, Patrick Meenan, Leonardo Losoviz, Andy Davies, Rachel Andrew, Anselm Hannemann, Patrick Hamann, Andy Davies, Tim Kadlec, Rey Bango, Matthias Ott, Peter Bowyer, Phil Walton, Mariana Peralta, Philipp Tellis, Ryan Townsend, Ingrid Bergman, Mohamed Hussain S. H., Jacob Groß, Tim Swalling, Bob Visser, Kev Adamson, Adir Amsalem, Aleksey Kulikov and Rodney Rehm for reviewing this article, as well as our fantastic community which has shared techniques and lessons learned from its work in performance optimization for everybody to use. You are truly smashing!

Smashing Editorial
(ra, il)

Source: Smashing Magazine, Front-End Performance Checklist 2019 [PDF, Apple Pages, MS Word]

Collective #481

dreamt up by webguru in Uncategorized | Comments Off on Collective #481





C481_FBT

FBT

FBT is an internationalization framework by Facebook designed to be both powerful and flexible, but also simple and intuitive.

Check it out


C481_Algorithms

Algorithms

A free electronic version of a soon to be published textbook by Jeff Erickson, along with other lecture notes he has written for various theoretical computer science classes at the University of Illinois, Urbana-Champaign since 1998.

Check it out







C481_biafont

Free Font: Biasachxua

Inspired by old Vietnamese book covers, Hiep Tong designed this typeface. It’s free for personal use and commercial use if you credit the author.

Get it








C481_static

Static Site Boilerplate

A boilerplate for static websites with automated build processes, a local development server, production minification and optimizations.

Check it out


C481_revolverfont

Free Font: Justice Regular

Described as “rough monospace type with cowboy boots”, the Justice font family is an outstanding gem. The regular style is free.

Get it

Collective #481 was written by Pedro Botelho and published on Codrops.


Source: Codrops, Collective #481

HTML5 SVG Fill Animation With CSS3 And Vanilla JavaScript

dreamt up by webguru in Uncategorized | Comments Off on HTML5 SVG Fill Animation With CSS3 And Vanilla JavaScript

HTML5 SVG Fill Animation With CSS3 And Vanilla JavaScript

HTML5 SVG Fill Animation With CSS3 And Vanilla JavaScript

Marina Ferreira



SVG stands for Scalable Vector Graphics and it is a standard XML-based markup language for vector graphics. It allows you to draw paths, curves, and shapes by determining a set of points in the 2D plane. Moreover, you can add twitch properties on those paths (such as stroke, color, thickness, fill, and more) in order to produce animations.

Since April 2017, CSS Level 3 Fill and Stroke Module allow SVG colors and fill patterns to be set from an external stylesheet, instead of setting attributes on each element. In this tutorial, we will use a simple plain hex color, but both fill and stroke properties also accept patterns, gradients and images as values.

Note: When visiting the Awwwards website, the animated note display can only be viewed with browser width set to 1024px or more.

Note Display Project Demo
A demo of the final result (Large preview)

File Structure

Let’s start by creating the files in the terminal:

🌹  mkdir note-display
🌹  cd note-display
🌹  touch index.html styles.css scripts.js

HTML

Here is the initial template that links both css and js files:

<html lang="en">
<head>
  <meta charset="UTF-8">

  <title>Note Display</title>

  <link rel="stylesheet" href="./styles.css">
</head>
<body>
  http://./scripts.js
</body>
</html>

Each note element consists of a list item: li that holds the circle, the note value, and its label.


List item element and direct children
List item element and its direct children: .circle, .percent and .label. (Large preview)

The .circle_svg is an SVG element, that wraps two <circle> elements. The first is the path to be filled while the second is the fill that will be animated.


SVG elements
SVG elements. SVG wrapper and circle tags. (Large preview)

The note is separated into integer and decimals so different font sizes can be applied to them. The label is a simple <span>. So, putting all of this together looks like this:

<li class="note-display">
  
0. 00
</div> <span class="label">Transparent</span> </li>

The cx and cy attributes define the circle’s x-axis and y-axis center point. The r attribute defines its radius.

You have probably noticed the underscore/dash pattern in classes names. That’s BEM, which stands for block, element and modifier. It is a methodology that makes your element naming more structured, organized and semantic.

Recommended reading: An Explanation Of BEM And Why You Need It

To finish the template structures, let’s wrap the four list items in an unordered list element:


Unordered list wrapper
Unordered list wrapper holds four li children (Large preview)
<ul class="display-container">
  <li class="note-display">
    
0. 00
</div> <span class="label">Transparent</span> </li> <li class="note-display">
0. 00
</div> <span class="label">Reasonable</span> </li> <li class="note-display">
0. 00
</div> <span class="label">Usable</span> </li> <li class="note-display">
0. 00
</div> <span class="label">Exemplary</span> </li> </ul>

You must be asking yourself what the labels Transparent, Reasonable, Usable and Exemplary mean. The more acquainted you get with programming, you will realize that writing code is not only about making the application functional, but also assuring that it will be long-term maintainable and scalable. That is only achieved if your code is easy to change.

“The acronym TRUE should help decide if the code you write will be able to accommodate change in the future or not.”

So, next time, ask yourself:

  • Transparent: Are code changes consequences clear?
  • Reasonable: Is cost benefit worth it?
  • Usable: Will I be able to reuse it in unexpected scenarios?
  • Exemplary: Does it present high quality as an example for future code?

Note: Practical Object-Oriented Design in Ruby” by Sandi Metz explains TRUE along with other principles and how to achieve those through design patterns. If you haven’t taken some time to study design patterns yet, consider adding this book to your bedtime reading.

CSS

Let’s import the fonts and apply a reset to all items:


@import url('https://fonts.googleapis.com/css?family=Nixie+One|Raleway:200');

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

The box-sizing: border-box property includes padding and border values into an element’s total width and height, so it’s easier to calculate its dimensions.

Note: For a visual explanation on box-sizing, please read “Make Your Life Easier With CSS Box Sizing.”

body {
  height: 100vh;
  color: #fff;
  display: flex;
  background: #3E423A;
  font-family: 'Nixie One', cursive;
}

.display-container {
  margin: auto;
  display: flex;
}

By combining the rules display: flex in the body and margin-auto in the .display-container, it’s possible to center the child element both vertically and horizontally. The .display-container element will also be a flex-container; that way, its children will be placed in the same row along the main axis.

The .note-display list item will also be a flex-container. Since there are many children for centering, let’s do it through the justify-content and align-items properties. All flex-items will be centered along the cross and main axis. If you’re not sure what those are, check out the alignment section at “CSS Flexbox Fundamentals Visual Guide.”

.note-display {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 0 25px;
}

Let’s apply a stroke to the circles by setting the rules stroke-width, stroke-opacity and stroke-linecap that altogether style the stroke live ends. Next, let’s add a color to each circle:

.circle__progress {
  fill: none;
  stroke-width: 3;
  stroke-opacity: 0.3;
  stroke-linecap: round;
}

.note-display:nth-child(1) .circle__progress { stroke: #AAFF00; }
.note-display:nth-child(2) .circle__progress { stroke: #FF00AA; }
.note-display:nth-child(3) .circle__progress { stroke: #AA00FF; }
.note-display:nth-child(4) .circle__progress { stroke: #00AAFF; }

In order to position the percent element absolutely, it’s necessary to know absolutely to what. The .circle element should be the reference, so let’s add position: relative to it.

Note: For a deeper, visual explanation on absolute positioning, please read “How To Understand CSS Position Absolute Once And For All.”

Another way of centering elements is to combine top: 50%, left: 50% and transform: translate(-50%, -50%); which position the element’s center at its parent’s center.

.circle {
  position: relative;
}

.percent {
  width: 100%;
  top: 50%;
  left: 50%;
  position: absolute;
  font-weight: bold;
  text-align: center;
  line-height: 28px;
  transform: translate(-50%, -50%);
}

.percent__int { font-size: 28px; }
.percent__dec { font-size: 12px; }

.label {
  font-family: 'Raleway', serif;
  font-size: 14px;
  text-transform: uppercase;
  margin-top: 15px;
}

By now, the template should be looking like this:


Finished initial template
Finished template elements and styles (Large preview)

Fill Transition

The circle animation can be created with the help of two circle SVG properties: stroke-dasharray and stroke-dashoffset.

stroke-dasharray defines the dash-gap pattern in a stroke.”

It can take up to four values:

  • When it’s set to an only integer (stroke-dasharray: 10), dashes and gaps have the same size;
  • For two values (stroke-dasharray: 10 5), the first is applied to dashes, second to gaps;
  • The third and forth forms (stroke-dasharray: 10 5 2 and stroke-dasharray: 10 5 2 3) will generate dashes and gaps in various sizes.
Stroke dasharray property values
stroke-dasharray property values (Large preview)

The image to the left shows the property stroke-dasharray being set from 0 to 238px, which is the circle circumference length.

The second image represents the stroke-dashoffset property that offsets the beginning of the dash array. It is also set from 0 to the circle circumference length.

Stroke dasharray and dashoffset properties
stroke-dasharray and stroke-dashoffset properties (Large preview)

To produce the filling effect, we will set the stroke-dasharray to the circumference length, so that all of its length gets filled with a big dash and no gap. We’ll also offset it by the same value, so it gets “hidden”. Then the stroke-dashoffset will be updated to the corresponding note value, filling the stroke accordingly to the transition duration.

The properties updating will be done in the scripts through CSS Variables. Let’s declare the variables and set the properties:

.circle__progress--fill {
  --initialStroke: 0;
  --transitionDuration: 0;
  stroke-opacity: 1;
  stroke-dasharray: var(--initialStroke);
  stroke-dashoffset: var(--initialStroke);
  transition: stroke-dashoffset var(--transitionDuration) ease;
}

In order to set the initial value and update the variables, let’s start by selecting all .note-display elements with document.querySelectorAll. The transitionDuration will be set to 900 milliseconds.

Then, we iterate through the displays array, select its .circle__progress.circle__progress--fill and extract the r attribute set in the HTML to calculate the circumference length. With that, we can set the initial --dasharray and --dashoffset values.

The animation will occur when the --dashoffset variable gets updated by a 100ms setTimeout:

const displays = document.querySelectorAll('.note-display');
const transitionDuration = 900;

displays.forEach(display => {
  let progress = display.querySelector('.circle__progress--fill');
  let radius = progress.r.baseVal.value;
  let circumference = 2 * Math.PI * radius;

  progress.style.setProperty('--transitionDuration', `${transitionDuration}ms`);
  progress.style.setProperty('--initialStroke', circumference);

  setTimeout(() => progress.style.strokeDashoffset = 50, 100);
});

To get the transition starting from the top, the .circle__svg element has to be rotated:

.circle__svg {
  transform: rotate(-90deg);
}
Stroke properties transition
Stroke properties transition (Large preview)

Now, let’s calculate the dashoffset value — relative to the note. The note value will be inserted to each li item through the data-* attribute. The * can be switched for any name that suits your needs and it can then, be retrieved in JavaScript through the element’s dataset: element.dataset.*.

Note: You can read more about the data-* attribute on MDN Web Docs.

Our attribute will be called “data-note”:

<ul class="display-container">
+ <li class="note-display" data-note="7.50">
    
0. 00
</div> <span class="label">Transparent</span> </li> + <li class="note-display" data-note="9.27">
0. 00
</div> <span class="label">Reasonable</span> </li> + <li class="note-display" data-note="6.93">
0. 00
</div> <span class="label">Usable</span> </li> + <li class="note-display" data-note="8.72">
0. 00
</div> <span class="label">Exemplary</span> </li> </ul>

The parseFloat method will convert the string returned by display.dataset.note into a floating point number. The offset represents the percentage missing to reach the maximum score. So, for a 7.50 note, we would have (10 - 7.50) / 10 = 0.25, which means the circumference length should be offset by 25% of its value:

let note = parseFloat(display.dataset.note);
let offset = circumference * (10 - note) / 10;

Updating the scripts.js:

const displays = document.querySelectorAll('.note-display');
const transitionDuration = 900;

displays.forEach(display => {
  let progress = display.querySelector('.circle__progress--fill');
  let radius = progress.r.baseVal.value;
  let circumference = 2 * Math.PI * radius;
+ let note = parseFloat(display.dataset.note);
+ let offset = circumference * (10 - note) / 10;

  progress.style.setProperty('--initialStroke', circumference);
  progress.style.setProperty('--transitionDuration', `${transitionDuration}ms`);

+ setTimeout(() => progress.style.strokeDashoffset = offset, 100);
});
Stroke properties transition up to note value
Stroke properties transition up to note value (Large preview)

Before we move on, let’s extract the stoke transition to its own method:

const displays = document.querySelectorAll('.note-display');
const transitionDuration = 900;

displays.forEach(display => {
- let progress = display.querySelector('.circle__progress--fill');
- let radius = progress.r.baseVal.value;
- let circumference = 2 * Math.PI * radius;
  let note = parseFloat(display.dataset.note);
- let offset = circumference * (10 - note) / 10;

- progress.style.setProperty('--initialStroke', circumference);
- progress.style.setProperty('--transitionDuration', `${transitionDuration}ms`);

- setTimeout(() => progress.style.strokeDashoffset = offset, 100);

+ strokeTransition(display, note);
});

+ function strokeTransition(display, note) {
+   let progress = display.querySelector('.circle__progress--fill');
+   let radius = progress.r.baseVal.value;
+   let circumference = 2 * Math.PI * radius;
+   let offset = circumference * (10 - note) / 10;

+   progress.style.setProperty('--initialStroke', circumference);
+   progress.style.setProperty('--transitionDuration', `${transitionDuration}ms`);

+   setTimeout(() => progress.style.strokeDashoffset = offset, 100);
+ }

Note Value Increase

There is still the note transition from 0.00 to the note value to be built. The first thing to do is to separate the integer and decimal values. We will use the string method split() (it takes an argument that determines where the string will be broken and returns an array containing both broken strings). Those will be converted to numbers and passed as arguments to the increaseNumber() function, along with the display element and a flag indicating if its an integer or a decimal.

const displays = document.querySelectorAll('.note-display');
const transitionDuration = 900;

displays.forEach(display => {
  let note = parseFloat(display.dataset.note);
+ let [int, dec] = display.dataset.note.split('.');
+ [int, dec] = [Number(int), Number(dec)];

  strokeTransition(display, note);

+ increaseNumber(display, int, 'int');
+ increaseNumber(display, dec, 'dec');
});

In the increaseNumber() function, we select either the .percent__int or .percent__dec element, depending on the className, and also in case the output should contain a decimal point or not. We’ve set our transitionDuration to 900ms. Now, to animate a number from 0 to 7, for example, the duration has to be divided by the note 900 / 7 = 128.57ms. The result represents how long each increase iteration will take. This means our setInterval will fire every 128.57ms.

With those variables set, let’s define the setInterval. The counter variable will be appended to the element as text and increased on each iteration:

function increaseNumber(display, number, className) {
  let element = display.querySelector(`.percent__${className}`),
      decPoint = className === 'int' ? '.' : '',
      interval = transitionDuration / number,
      counter = 0;

  let increaseInterval = setInterval(() => {
    element.textContent = counter + decPoint;
    counter++;
  }, interval);
}
Infinite counter increase
Infinite counter increase (Large preview)

Cool! It does increase the values, but it kind of does it forever. We need to clear the setInterval when the notes achieve the value we want. That is done with clearInterval function:

function increaseNumber(display, number, className) {
  let element = display.querySelector(`.percent__${className}`),
      decPoint = className === 'int' ? '.' : '',
      interval = transitionDuration / number,
      counter = 0;

  let increaseInterval = setInterval(() => {
+   if (counter === number) { window.clearInterval(increaseInterval); }

    element.textContent = counter + decPoint;
    counter++;
  }, interval);
}
Finished note display project
Finished project (Large preview)

Now the number is updated up to the note value and cleared with clearInterval() function.

That’s pretty much it for this tutorial. I hope you enjoyed it!

If you feel like building something a bit more interactive, check out my Memory Game Tutorial created with Vanilla JavaScript. It covers basic HTML5, CSS3 and JavaScript concepts such as positioning, perspective, transitions, Flexbox, event handling, timeouts and ternaries.

Happy coding! 🌹

Smashing Editorial
(dm, ra, il)

Source: Smashing Magazine, HTML5 SVG Fill Animation With CSS3 And Vanilla JavaScript

Collective #480

dreamt up by webguru in Uncategorized | Comments Off on Collective #480




C455_Divi

Our Sponsor

Real Time Design in WordPress

Divi is powered by the Divi Builder, an insanely fast and incredibly intuitive front end editor like nothing you have seen before. It will change the way you build websites forever.

Check it out



C480_rr

rrweb

Rrweb is an open source web session replay library that provides easy-to-use APIs to record interactions and replay them remotely.

Check it out













C480_Buddience

Buddience

Students from Nara University in Japan have created this project where your face is matched to a Buddha statue image using Microsoft’s emotion analysis technology.

Check it out




Collective #480 was written by Pedro Botelho and published on Codrops.


Source: Codrops, Collective #480