You know that user feedback is crucial — after all, your users will decide whether your app succeeds or not — but how do you know whether users are being fair and objective in their feedback?
We can tell you: They won’t be. All of your users will be giving you biased feedback. They can’t help it.
When soliciting and listening to user feedback, you will inevitably run into bias on both sides of the coin: Biases will influence the people providing feedback, and your own biases will influence the way you receive that feedback.
It’s important to be aware of this, especially when reviewing comments about your user experience (UX). Accurate and unbiased feedback is essential to developing the best possible version of your app. Although you can’t erase your own biases (or those of your users), you can take steps to overcome common biases once you know what they are and how they might appear. The next time you ask your users for input, keep bias in mind and evaluate how you respond to users’ comments. Is your action (or inaction) driven by bias?
Collecting And Analyzing Data
When determining qualitative sample sizes in user research, researchers must 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. Read a related article →
There are dozens of cognitive biases that take many different forms, although a few dominating types emerge frequently for product teams seeking user feedback. In this article, we’ll take a closer look at four of the most common types of cognitive biases that pop up when collecting and interpreting UX feedback — and how you can nip these biases in the bud, before they skew your production process:
This is probably the most well-known bias encountered by people of all professions. Psychologist Daniel Kahneman, who first introduced the concept of confirmation bias together with mathematical psychologist Amos Tversky, says that confirmation bias exists “when you have an interpretation, and you adopt it, and then, top down, you force everything to fit that interpretation.” Confirmation bias occurs when you gravitate towards responses that align with your own beliefs and preconceptions.
Solely accepting feedback that aligns with your established narrative creates an echo chamber that will severely affect your approach to UX design. One dangerous effect of confirmation bias is the backfire effect, in which you begin to reject any results that prove your opinions wrong. As a designer, you are tasked with creating the UX that best serves your audience, but your design will be based in part on your subjective tastes, beliefs and background. Sometimes, as we learned firsthand, this bias can sneak its way into your process — not so much in how you interpret user feedback, but in how you ask for it.
In the early years of my agency designing web and mobile apps for clients, we used to have our UX designers write user surveys and conduct interviews to get feedback on products. After all, the designers understood the UX like no one else, and, ultimately, they’d be the ones to make any changes. Strangely, after doing this for about a year, we noticed that we weren’t getting a lot of actionable feedback. We began to doubt the value of even creating these surveys. Before tossing them out entirely, we experimented by removing the UX designers from the feedback process. We had one of our quality-assurance (QA) engineers write the user survey and collect the feedback — and we quickly found the results were vastly more interesting and actionable.
Although our UX designers were open to hearing feedback, we realized they were subconsciously formulating survey and interview questions in a manner that would easily confirm their own preconceptions about the design they created. For instance, our UX designers asked, “Did the wide variety of products available make it difficult to find the specific product you wanted?” This phrasing led our respondents to perceive that finding a product was difficult in the first place — leaving no room for those who found it easy to reflect that in their answers. The question also suggested a cause of difficulty (the wide variety of products), leaving no room for respondents to offer other potential reasons for difficulty finding a product.
When our QA engineers took the reins, they wrote the question as, “Did you have any difficulty finding the product you wanted? If so, why?” Having no strong preexisting beliefs about the design, they posed an unbiased question — leaving room for more genuine answers from the respondents who had difficulty finding products, as well as those who didn’t. By following up with an open-ended “Why?,” we were able to collect more diverse and informative answers, helping us to learn more about the various reasons that respondents found difficulty with our design.
Confirmation bias often shows up when one is creating user surveys. In your survey, you might inadvertently ask leading questions, phrased in a way that generates answers that validate what you already believe. Our UX designers asked leading questions like, “Did the branding provide a sense of professionalism and trust?” Questions like this don’t allow space for users to provide negative or opposing feedback. By removing our designers from the feedback process, the questions naturally became less biased in phrasing — our QA engineers asked non-leading questions like, “What sort of impressions did the app’s look and feel provide?” As a result, we began to see far more objective and truly helpful feedback coming from users.
Avoiding Confirmation Bias
Overcoming confirmation bias requires collecting feedback from a diverse group of people. The bigger the pool of users providing feedback, the more perspectives you add to the mix. Don’t survey or interview users from only one group, demographic or background — do your best to get a large sample size filled with users who represent all demographics in your target market. This way, the feedback you receive won’t be limited to one group’s set of preconceptions.
Write survey questions carefully to avoid leading questions. Instead of asking, “How much did you like feature X of the app?,” you might ask, “Rate your satisfaction with feature X on the following scale” (and provide a scale that ranges from “strongly dislike” to “strongly like”). The first phrasing suggests that the user should like the feature in question, whereas the second phrasing doesn’t make an inherent suggestion. Have someone else read your survey questions before sending them out to users, to check that they sound impartial.
UX designers can also avoid falling prey to confirmation bias by using more quantitative data — although, as you’ll see below, even interpretation of numerical data isn’t immune to bias.
Framing Bias
Framing bias is based on how you choose to frame the user feedback you’ve received. This kind of bias can make a designer interpret an objective metric in a positive or negative light. Nielsen Norman Group offers a fascinating example that describes the results of a user feedback survey in two ways. In the first, 4 out of 20 users said they could not find the search function on a website; in the second, 16 out of 20 users stated that they found the search function.
Nielsen Norman Group presented these two results — which communicate the same information — to a group of UX designers, with half receiving the success rate (16 out of 20) and half the failure rate (4 out of 20). It found that only 39% of those who received the positive statement were likely to call for a redesign, compared to 51% of respondents who received the failure rate. Despite the metric being the same, a framing bias led to these professional UX designers behaving differently.
The presence of framing bias when analyzing data can lead to subsequent effects, such as the clustering illusion, in which people mistakenly perceive patterns in data that are actually coincidental, or the anchoring effect, in which people give much more weight to the first piece of data they look at than the rest of the data. These mental traps can influence the decisions you make in the best interest of the product.
Avoiding Framing Bias
You can avoid framing bias by becoming more self-aware of how you look at data — and by adding more frames.
For every piece of feedback you assess, ask yourself how you’re framing the data. This awareness will help you learn not to take your first interpretation as a given, and to understand why your perspective feels positive or negative. Then, identify at least one or two alternative frames you could use to phrase the same result. Let’s say one of your survey results shows that 70% of users feel your UI is intuitive. This gives you a surge of pride and validation, but you also recognize that it’s framed as a positive. Using an alternative frame, you write the result again as such: 30% of users do not feel the UI is intuitive. By looking at both frames, you gain a less biased and more well-rounded perception of what this data means for your product.
Be wise enough to admit when you’re unsure of what action to take based on your data. Get a second opinion from someone on your team. If one piece of feedback seems particularly important and difficult to interpret, consider sending out a new survey or gathering more feedback on that topic. Perhaps you could ask those users who don’t feel your UI is intuitive to elaborate on specific aspects of the UI (colors, button placement, text, etc.). What specific, impartial questions could you create to gain deeper insight from users?
Friendliness Bias
Of course, you want to be civil and professional with the people who provide UX feedback, but it doesn’t pay to be too friendly. In other words, you don’t want to be so accommodating that it skews their responses.
Friendliness bias — also called acquiescence bias or user research bias — occurs when the people providing feedback tell you the answers they think you want to hear. Sometimes, this happens because they think fondly of you and respect your professional opinion, but the reason can also be less flattering.
People might tell you what you want to hear because they’re tired of being questioned, and they think positive answers will get them out of the room (or out of the online survey) faster. This is the principle of least effort, which states that people will try to use the smallest amount of thought, time and energy to avoid resistance and complete a task. This principle has probably already influenced the usability of your UX design, but you might not have considered how it comes into play when collecting feedback.
Whatever the cause, friendliness bias can tarnish the hard work and market research you’ve conducted, giving you ungenuine data you can’t effectively use.
Avoiding Friendliness Bias
Friendliness bias can be avoided by removing yourself from the picture, because most people don’t like to give unfavorable feedback face to face.
If gathering UX feedback involves in-person questionnaires or focus groups, have someone outside of your development team serve as a facilitator. The facilitator should make it clear that he or she is not the one responsible for the design of the product. This way, people might feel more comfortable providing honest — and negative — feedback.
Collecting feedback digitally is also a helpful way to reduce the chance of your data being compromised by friendliness bias. People might open up more when sitting behind a screen, because they don’t have to face the reactions of the survey’s providers.
Be mindful, especially if you go the digital route, of survey fatigue. When you ask too many questions, your users might begin to tire partway through the survey. This can result in people simply selecting answers at random (or choosing the most favorable answers) just to finish faster and expend the least amount of effort. To avoid friendliness bias due to survey fatigue, keep surveys as short as possible and phrase the questions in very simple terms. Don’t make all questions required, and edit the survey diligently to cut all questions that aren’t truly relevant or necessary.
False-Consensus Bias
This form of bias happens when developers overestimate the number of people who will agree with their idea or design, creating a false consensus. Put simply, false consensus is the assumption that others will think the same way as you.
A 1976 Stanford study asked 104 college students to walk around campus wearing a sandwich board advertising a restaurant. The study found that 62% of those who agreed to wear the sign believed others would respond the same way, and that 67% of students who refused to wear the sign thought their peers would also respond negatively. The fact that both of these groups formed a majority in favor of their personal belief is an example of false-consensus bias.
As outlined above, our own UX designers fell into false-consensus bias when writing user survey questions, unintentionally phrasing questions with the assumption that users would appreciate the same UX features that the designers appreciated. Even though the core goal in UX design is to set aside your personal beliefs in favor of the wants and needs of your audience, you can’t help but see your product through your own lens — making it difficult to imagine that others would see it any other way. This underscores the importance of having team members with different backgrounds (especially those with expertise outside of UX design) be involved in the feedback process.
Avoiding False-Consensus Bias
False-consensus bias can be avoided by identifying and articulating your own assumptions. When you begin creating a user survey or crafting a test group, ask yourself, “What do I think the results of this feedback will be?” Write them down — these are your assumptions. Even better, ask a friend or coworker to listen to you describe the product, and have them write down the assumptions and opinions they hear from you.
Once you’re aware of your perceptions, you can design the feedback process to ensure you don’t favor your own opinions. With your assumptions sitting in front of you, try this exercise: Pretend every single one of your assumptions is wrong. If this is the case, which of these assumptions would be the riskiest to your product’s success? Which ones would cause widespread dissatisfaction across users? Craft questions for your users that challenge those risky assumptions.
Just as with confirmation bias, it’s important to collect feedback from a wide range of users, across all demographics and backgrounds. Ensure you’re not just surveying people who work closely with you or who come from a similar background — these people are likely to share the same opinions and preconceptions as you, which can reinforce your false consensus.
Breaking Down Biases Makes User Feedback More Valuable
Bias is universal, but so too are the methods you can take to avoid it. People — including you — can’t lose their own biases, but that doesn’t mean you have to let them interfere with your work. By simply understanding what each bias is and by breaking down the ways that it appears during the feedback process, you can put measures in place to overcome misleading preconceptions and gather the most impartial feedback possible.
Ensure that all of your questions are carefully worded and edited, because this will improve clarity and maintain participant focus. To avoid skewed data, always involve as large and diverse a group as possible, and try to remove yourself from the feedback — both in person as the facilitator of the survey or test group, and emotionally as the reviewer of the comments. This will encourage participants to answer more honestly about their experience with your UX, while preventing you from projecting your own assumptions and framing onto their feedback.
You might find it helpful to bring in a second opinion on the reviews as you start to establish bias awareness as a permanent part of your feedback and testing practices. It’s not an easy process, but when you can reduce the influence of bias on your work, you can get to what really matters: designing a better experience for your users.
With so many JavaScript frameworks around, single-page application (SPA) websites seem to be all the rage nowadays. However, an SPA architecture has the drawback of having a slower first-page load than a server-based application, because all of the JavaScript templates used to render the HTML view must be downloaded before the required view can be generated.
Enter service workers. Through service workers, all framework and application code to output the HTML view can be precached in the browser, thus speeding up both the first meaningful paint and the time to interact. In this article, I will share my experience with implementing service workers for PoP, an SPA website that runs on WordPress, with the goal of speeding up the loading time and providing offline-first capabilities.
Most of the code from the explanation below can be reused to create a solution for traditional (i.e. non-SPA) WordPress websites, too. Anyone want to implement a plugin?
Defining The Application’s Features
In addition to being suitable for an SPA WordPress website, the implementation of service workers below has been designed to support the following features:
loading of assets from external sources, such as a content delivery network (CDN);
multilingual (i18n) support;
multiple views;
selection on runtime of the caching strategy, on a URL-by-URL basis.
Based on this, we’ll make the following design decisions.
Load an Application Shell (or Appshell) First
If the SPA architecture supports it, load an appshell first (i.e. minimal HTML, CSS and JavaScript to power the user interface), under https://www.mydomain.com/appshell/. Once loaded, the appshell will dynamically request content from the server through an API. Because all of the appshell’s assets can be precached using service workers, the website’s frame will load immediately, speeding up the first meaningful paint. This scenario uses the “cache falling back to network” caching strategy.
Watch out for conflicts! For instance, WordPress outputs code that is not supposed to be cached and used forever, such as nonces, which usually expire after 24 hours. The appshell, which service workers will cache in the browser for more than 24 hours, needs to deal with nonces properly.
Extract WordPress Resources Added Through wp_enqueue_script and wp_enqueue_style
Because a WordPress website loads its JavaScript and CSS resources through the wp_enqueue_script and wp_enqueue_style hooks, respectively, we can conveniently extract these resources and add them to the precache list.
While following this strategy reduces the effort to produce the list of resources to precache (some files will still need to be added manually, as we shall see later), it implies that the service-workers.js file must be generated dynamically, on runtime. This suits very well the decision to enable WordPress plugins to hook into the generation of the service-workers.js file, as explained in the next item.
Indeed, I’d argue that there is no other way but to hook into these functions, because to generate the list manually (i.e. finding and listing all resources loaded by all plugins and the theme) is too troublesome of a process, and using other tools to generate the JavaScript and CSS files, such as Service Worker Precache, will actually not work in this context, for two main reasons:
Service Worker Precache works by scanning files in a specified folder and filtering them using wildcards. However, the files that WordPress ships with are many more than the actual ones required by the application, so we would quite likely be precaching plenty of redundant files.
WordPress attachs a version number to the requested file, which varies from file to file, such as:
Service workers intercept each request based on the full path of the requested resource, including the parameters — such as the version number, in this case. Because the Service Worker Precache tool is not aware of the versioning number, it will be unable to produce the required list correctly.
Allow Plugins to Hook Into the Generation of service-workers.js
Adding hooks into our functionality enables us to extend the functionality of service workers. For instance, third-party plugins can hook into the precached resources list to add their own resources, or to specify which caching strategy to use depending on their URL pattern, among others.
Caching External Resources: Define a List of Domains, and Validate the Resources to Precache Originate From Any of These.
Whenever the resource originates from the website’s domain, it can always be handled using service workers. Whenever not, it can still be fetched but we must use no-cors fetch mode. This type of request will result in an opaque response, so we won’t be able to check whether the request was successful; however, we can still precache these resources and allow the website to be browsable offline.
Support for Multiple Views
Let’s assume the URL contains a parameter indicating which view to use to render the website, such as:
Several appshells can be precached, each one representing a view:
https://www.mydomain.com/appshell/?view=default
https://www.mydomain.com/appshell/?view=embed
https://www.mydomain.com/appshell/?view=print
Then, when loading the website, we extract the value of the view parameter from the URL, and load the corresponding appshell, on runtime.
i18n Support
We are assuming that the language code is part of the URL, like this: https://www.mydomain.com/language-code/path/to/the/page/
One possible approach would be to design the website to provide a different service-workers.js file for each language, each to be employed under its corresponding language scope: a service-worker-en.js file for the en/ scope for English, a service-worker-es.js for the es/ scope for Spanish, and so on. However, conflicts arise when accessing shared resources, such as the JavaScript and CSS files located in the wp-content/ folder. These resources are the same for all languages; their URLs don’t carry any information about language. Adding yet another service-workers.js file to deal with all non-language scopes would add undesired complexity.
A more straightforward approach would be to employ the same technique as above for rendering multiple views: Register a unique service-workers.js file that already contains all information for all languages, and decide on runtime which language to use by extracting the language code from the requested URL.
In order to support all of the features described so far for, say, three views (a default, embed and print view) and two languages (English and Spanish), we would need to generate the following appshells:
The application must be able to choose from several caching strategies in order to support different behaviors on different pages. Here are some possible scenarios:
A page can be retrieved from cache most of the time, but on specific occasions it must be retrieved from the network (for example, to view a post after editing it).
A page may have a user state and, as such, cannot be cached (for example, “Edit my account,” “My posts”).
A page can be requested in the background to bring extra data (for example, lazy-loaded comments on a post).
Here are the caching strategies and when to use each:
Static assets (JavaScript and CSS files, images, etc.)
The static content will never be updated: JavaScript and CSS files have the versioning number, and uploading the same image a second time into WordPress’ media manager will change the image’s file name. As such, cached static assets will not become stale.
Appshell
We want the appshell to load immediately, so we retrieve it from the cache. If the application is upgraded and the appshell changes, then changing the version number will install a new version of the service worker and download the latest version of the appshell.
While getting the content from the cache to display it immediately, we also send the request to bring the content from the server. We compare the two versions using an ETag header, and if the content has changed, we cache the server’s response (i.e. the most up to date of the two) and then show a message to the user, “This page has been updated, please click here to refresh it.”
Content that would normally use the “cache then network” strategy can be forced to use a “network only” strategy by artificially adding the parameter sw-strategy=networkfirst or sw-networkfirst=true to the requested URL. This parameter can be removed before the request is sent to the server.
Content with user state
We do not want to cache any content with a user state, due to security. (We could delete the user state cache when the user signs out, but implementing that is more complex.)
Content is lazy-loaded when the user will not see it immediately, allowing the content that the user sees immediately to load faster. (For example, a post would load immediately, and its comments would be lazy-loaded because they appear at the bottom of the page.) Because it will not be seen immediately, fetching this content straight from the cache is not necessary either; instead, always try to get the most up-to-date version from the server.
Ignore Certain Data When Generating the ETag Header for the “Cache Then Network” Strategy
An ETag can be generated using a hash function; a very tiny change in the input will produce a completely different output. As such, values that are not considered important and that are allowed to become stale should not be factored in when generating the ETag. Otherwise, the user might be prompted with the message “This page has been updated, please click here to refresh it” for every tiny change, such as the comments counter going from 5 to 6.
We have decided to automatically extract all JavaScript and CSS files that are to be used by the application, added through the wp_enqueue_script and wp_enqueue_style functions, in order to export these resources into the Service Worker Precache list. This implies that the service-workers.js file will be generated on runtime.
When and how should it be generated? Triggering an action to create the file through admin-ajax.php (for example, calling https://www.mydomain.com/wp-admin/admin-ajax.php?action=create-sw) will not work, because it would load WordPress’ admin area. Instead, we need to load all JavaScript and CSS files from the front end, which will certainly be different.
A solution is to create a private page on the website (hidden from view), accessed through https://www.mydomain.com/create-sw/, which will execute functionality to create the service-workers.js file. The file’s creation must take place at the very end of the execution of the request, so that all JavaScript and CSS files will have been enqueued by then:
function generate_sw_shortcode($atts) { add_action('wp_footer', 'generate_sw_files', PHP_INT_MAX);}add_shortcode('generate_sw', 'generate_sw_shortcode');
File: sw.php
Please note that this solution works because the website is an SPA which loads ALL files in advance for the whole lifecycle of the application usage (the infamous bundled file); requesting any 2 different URLs from this website will always load the same set of .js and .css files. I am currently developing code-splitting techniques into the framework which will, coupled with HTTP/2, load only the required JS resources and nothing else, on a page-by-page basis — it should be ready within a couple of weeks. Hopefully I will then be able to describe how Service Workers + SPA + code-splitting can work all together.
The generated file could be placed in the root of the website (i.e. alongside wp-config.php) to grant it / scope. However, placing files in the root folder of the website is not always advisable, such as for security (the root folder must have very restrictive write permissions) and for maintenance (if service-workers.js were to be generated by a plugin and this one was disabled because its folder was renamed, then the service-workers.js file might never get deleted).
Luckily, there is another possibility. We can place the service-workers.js file in any directory, such as wp-content/sw/, and add an .htaccess file that grants access to the root scope:
function generate_sw_files() { $dir = WP_CONTENT_DIR."/sw/" // Create the directory structure if (!file_exists($dir)) { @mkdir($dir, 0755, true); } // Generate Service Worker .js file save_file($dir.'service-workers.js', get_sw_contents()); // Generate the file to register the Service Worker save_file($dir.'sw-registrar.js', get_sw_registrar_contents()); // Generate the .htaccess file to allow access to the scope (/) save_file($dir.'.htaccess', get_sw_htaccess_contents());}function get_sw_registrar_contents() { return ' if ("serviceWorker" in navigator) { navigator.serviceWorker.register("/wp-content/sw/service-workers.js", { scope: "/" }); } ';}function get_sw_htaccess_contents() { return ' <FilesMatch "service-workers.js$"> Header set Service-Worker-Allowed: / </FilesMatch> ';}function save_file($file, $contents) { // Open the file, write content and close it $handle = fopen($file, "wb"); $numbytes = fwrite($handle, $contents); fclose($handle); return $file;}
File: sw.php
Generation of these files can be included during the website deployment process, in order to automate it and so that all files are created just before the new website version becomes available to users.
For security reasons, we can add some validation in function generate_sw_files() before it executes:
to provide a valid access key as a parameter.
to make sure it can only be requested from within the same server.
From an array of servers behind a load balancer, or from a stack of servers in the cloud using auto-scaling, we can’t execute wget URL, because we don’t know which server will serve the request. Instead, we can directly execute the PHP process, using php-cgi:
If you’re uncomfortable with having this page on a production server, this process could also be run in a staging environment, as long as it has exactly the same configuration as the production server (i.e. the database must have the same data; all constants in wp-config.php must have the same values; the URL to access the website on the staging server must be the same as the website itself, etc.). Then, the newly created service-workers.js file must be copied from the staging to production servers during deployment of the website.
Contents of service-workers.js
Generating service-workers.js from a PHP function implies that we can provide a template of this file, which will declare what variables it needs, and the service worker’s logic. Then, on runtime, the variables will be replaced with actual values. We can also conveniently add hooks to enable plugins to add their own required values. (More configuration variables will be added later on in this article).
function get_sw_contents() { // $sw_template has the path to the service-worker template $sw_template = dirname(__FILE__).'/assets/sw-template.js'; $contents = file_get_contents($sw_template); foreach (get_sw_configuration() as $key => $replacement) { $value = json_encode($replacement); $contents = str_replace($key, $value, $contents); } return $contents;}function get_sw_configuration() { $configuration = array(); $configuration['$version'] = get_sw_version(); … return $configuration;}
File: sw.php
The configuration of the service workers template looks like this:
var config = { version: $version, …};
File: sw-template.js
Resource types
The introduction of resource types, which is a way of splitting assets into groups, allows us to implement different behaviors in the logic of the service worker. We will need the following resource types:
HTML
Produced only when first loading the website. After that, all content is dynamically requested using the application API, whose response is in JSON format
JSON
The API to get and post content
Static
Any asset, such as JavaScript, CSS, PDF, an image, etc.
Resource types can be used for caching resources, but this is optional (it only makes the logic more manageable). They are needed for:
selecting the appropriate caching strategy (for static, cache-first, and for JSON, network-first);
defining paths not to intercept (anything under wp-content/ for JSON but not for static, or anything ending in .php for static, for dynamically generated images, but not for JSON).
function get_sw_resourcetypes() { return array('static', 'json', 'html');}
File: sw.php
function getResourceType(request) { var acceptHeader = request.headers.get('Accept'); var resourceType = 'static'; if (acceptHeader.indexOf('text/html') !== -1) { resourceType = 'html'; } else if (acceptHeader.indexOf('application/json') !== -1) { resourceType = 'json'; } return resourceType;}
File: sw-template.js
Intercepting requests on service workers
We will define for which URL patterns we do not want the service worker to intercept the request. The list of resources to exclude is initially empty, just containing a hook to inject all values.
$excludedFullPaths
Full paths to exclude.
$excludedPartialPaths
Paths to exclude, appearing after the home URL (for example, articles will exclude https://www.mydomain.com/articles/ but not https://www.mydomain.com/posts/articles/). Partial paths are useful when the URL contains language information (for example, https://www.mydomain.com/en/articles/), so a single path would exclude that page for all languages (in this case, the home URL would be https://www.mydomain.com/en/). More on this later.
The value opts.locales.domain will be calculated on runtime (more on this later).
var config = { … excludedPaths: { full: $excludedFullPaths, partial: $excludedPartialPaths }, …};self.addEventListener('fetch', event => { function shouldHandleFetch(event, opts) { var request = event.request; var resourceType = getResourceType(request); var url = new URL(request.url); var fullExcluded = opts.excludedPaths.full[resourceType].some(path => request.url.startsWith(path)), var partialExcluded = opts.excludedPaths.partial[resourceType].some(path => request.url.startsWith(opts.locales.domain+path)); if (fullExcluded || partialExcluded) return false; if (resourceType == 'static') { // Do not handla dynamic images, eg: the Captcha image, captcha.png.php var isDynamic = request.url.endsWith('.php') && request.url.indexOf('.php?') === -1; if (isDynamic) return false; } … } …});
File: sw-template.js
Now we can define WordPress resources to be excluded. Please note that, because it depends on the resource type, we can define a rule to intercept any URL starting with wp-content/, which works only for the resource type “static.”
class PoP_ServiceWorkers_Hooks_WPExclude { function __construct() { add_filter('PoP_ServiceWorkers_Job_Fetch:exclude:full', array($this, 'get_excluded_fullpaths'), 10, 2); } function get_excluded_fullpaths($excluded, $resourceType) { if ($resourceType == 'json' || $resourceType == 'html') { // Do not intercept access to the WP Dashboard $excluded[] = admin_url(); $excluded[] = content_url(); $excluded[] = includes_url(); } elseif ($resourceType == 'static') { // Do not cache the service-workers.js file!!! $excluded[] = WP_CONTENT_DIR.'/sw/service-workers.js'; } return $excluded; }}new PoP_ServiceWorkers_Hooks_WPExclude();
Precaching resources
In order for the WordPress website to work offline, we need to retrieve the full list of resources needed and precache them. We want to be able to cache both local and external resources (for example, from a CDN).
$origins
Define from which domains we enable the service worker to intercept the request (for example, from our own domain plus our CDN).
$cacheItems
List of resources to precache. It is initially an empty array, providing a hook to inject all values.
var config = { … cacheItems: $cacheItems, origins: $origins, …};
In order to precache external resources, executing cache.addAll will not work. Instead, we need to use the fetch function, passing the parameter {mode: 'no-cors'} for these.
Resources to be intercepted with the service worker either must come from any of our origins or must have been defined in the initial precache list (so that we can precache assets from yet other external domains, such as https://cdnjs.cloudflare.com):
self.addEventListener('fetch', event => { function shouldHandleFetch(event, opts) { … var fromMyOrigins = opts.origins.indexOf(url.origin) > -1; var precached = opts.cacheItems[resourceType].indexOf(url) > -1; if (!(fromMyOrigins || precached)) return false; … } …});
File: sw-template.js
Generating the list of resources to precache
Assets loaded through wp_enqueue_script and script_loader_tag can be extracted easily. Finding other assets involves a manual process, depending on whether they are coming from WordPress core files, from the theme or from installed plugins:
images;
CSS and JavaScript not loaded through wp_enqueue_script and script_loader_tag;
JavaScript files conditionally loaded (such as those added between html tags);
resources requested on runtime (for example, TinyMCE’s theme, skin and plugin files);
references to JavaScript files hardcoded another JavaScript file;
Font files referenced in CSS files (TTF, WOFF, etc.);
locale files;
i18n files.
To retrieve all JavaScript files loaded through the wp_enqueue_script function, we would hook into script_loader_tag, and for all CSS files loaded through the wp_enqueue_style function, we would hook into style_loader_tag:
class PoP_ServiceWorkers_Hooks_WP { private $scripts, $styles, $dom; function __construct() { $this->scripts = $this->styles = array(); $this->doc = new DOMDocument(); add_filter('script_loader_tag', array($this, 'script_loader_tag')); add_filter('style_loader_tag', array($this, 'style_loader_tag')); … } function script_loader_tag($tag) { if (!empty($tag)) { $this->doc->loadHTML($tag); foreach($this->doc->getElementsByTagName('script') as $script) { if($script->hasAttribute('src')) { $this->scripts[] = $script->getAttribute('src'); } } } return $tag; } function style_loader_tag($tag) { if (!empty($tag)) { $this->doc->loadHTML($tag); foreach($this->doc->getElementsByTagName('link') as $link) { if($link->hasAttribute('href')) { $this->styles[] = $link->getAttribute('href'); } } } return $tag; } …}new PoP_ServiceWorkers_Hooks_WP();
Then, we simply add all of these resources to the precache list:
class PoP_ServiceWorkers_Hooks_WP { function __construct() { … add_filter('PoP_ServiceWorkers_Job_CacheResources:precache', array($this, 'get_precache_list'), 10, 2); } function get_precache_list($precache, $resourceType) { if ($resourceType == 'static') { $precache = array_merge( $precache, $this->scripts, $this->styles ); } return $precache; }}
WordPress will load a few files that must be manually added. Please note that the reference to the file must be added exactly as it will be requested, including all of the parameters. So, this process involves a lot of copying and pasting from the original code:
class PoP_ServiceWorkers_Hooks_WPManual { function __construct() { add_filter('PoP_ServiceWorkers_Job_CacheResources:precache', array($this, 'get_precache_list'), 10, 2); } function get_precache_list($precache, $resourceType) { if ($resourceType == 'static') { // File json2.min.js is not added through the $scripts list because it's 'lt IE 8' global $wp_scripts; $suffix = SCRIPT_DEBUG ? '' : '.min'; $this->scripts[] = add_query_arg('ver', '2015-05-03', $wp_scripts->base_url."/wp-includes/js/json2$suffix.js"); // Needed for the thickboxL10n['loadingAnimation'] javascript code produced in the front-end, loaded in wp-includes/script-loader.php $precache[] = includes_url('js/thickbox/loadingAnimation.gif'); } return $precache; }}new PoP_ServiceWorkers_Hooks_WPManual();
TinyMCE presents a tough challenge for obtaining its list of resources, because the files it loads (such as plugins, skins and theme files) are actually created and requested on runtime. Moreover, the full path of the resource is not printed in the HTML code, but is assembled in a JavaScript function. So, to obtain the list of resources, one can inspect TinyMCE’s source code and check how it generates the file names, or guess them by creating a TinyMCE editor while inspecting Chrome’s Developer Tools’ “Network” tab and seeing which files it requests. Doing the latter, I was able to deduce all file names (for example, for theme files, the path is a combination of the domain, theme name and versioning as parameters).
To obtain TinyMCE’s configuration that will be used on runtime, we hook into store_tinymce_resources and teeny_mce_before_init and inspect the values set in the $mceInit variable:
class PoP_ServiceWorkers_Hooks_TinyMCE { private $content_css, $external_plugins, $plugins, $others; function __construct() { $this->content_css = $this->external_plugins = $this->plugins = $this->others = array(); // Execute last one add_filter('teeny_mce_before_init', array($this, 'store_tinymce_resources'), PHP_INT_MAX, 1); add_filter('tiny_mce_before_init', array($this, 'store_tinymce_resources'), PHP_INT_MAX, 1); } function store_tinymce_resources($mceInit) { // Code copied from wp-includes/class-wp-editor.php function editor_js() $suffix = SCRIPT_DEBUG ? '' : '.min'; $baseurl = includes_url( 'js/tinymce' ); $cache_suffix = $mceInit['cache_suffix']; if ($content_css = $mceInit['content_css']) { foreach (explode(',', $content_css) as $content_css_item) { // The $cache_suffix is added in runtime, it can be safely added already. Eg: wp-includes/css/dashicons.min.css?ver=4.6.1&wp-mce-4401-20160726 $this->content_css[] = $content_css_item.'&'.$cache_suffix; } } if ($external_plugins = $mceInit['external_plugins']) { if ($external_plugins = json_decode($external_plugins, true)) { foreach ($external_plugins as $plugin) { $this->external_plugins[] = "{$plugin}?{$cache_suffix}"; } } } if ($plugins = $mceInit['plugins']) { if ($plugins = explode(',', $plugins)) { // These URLs are generated on runtime in TinyMCE, without a $version foreach ($plugins as $plugin) { $this->plugins[] = "{$baseurl}/plugins/{$plugin}/plugin{$suffix}.js?{$cache_suffix}"; } if (in_array('wpembed', $plugins)) { // Reference to file wp-embed.js, without any parameter, is hardcoded inside file wp-includes/js/tinymce/plugins/wpembed/plugin.min.js!!! $this->others[] = includes_url( 'js' )."/wp-embed.js"; } } } if ($skin = $mceInit['skin']) { // Must produce: wp-includes/js/tinymce/skins/lightgray/content.min.css?wp-mce-4401-20160726 $this->others[] = "{$baseurl}/skins/{$skin}/content{$suffix}.css?{$cache_suffix}"; $this->others[] = "{$baseurl}/skins/{$skin}/skin{$suffix}.css?{$cache_suffix}"; // Must produce: wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.woff $this->others[] = "{$baseurl}/skins/{$skin}/fonts/tinymce.woff"; } if ($theme = $mceInit['theme']) { // Must produce: wp-includes/js/tinymce/themes/modern/theme.min.js?wp-mce-4401-20160726 $this->others[] = "{$baseurl}/themes/{$theme}/theme{$suffix}.js?{$cache_suffix}"; } // Files below are always requested. Code copied from wp-includes/class-wp-editor.php function editor_js() global $wp_version, $tinymce_version; $version = 'ver=' . $tinymce_version; $mce_suffix = false !== strpos( $wp_version, '-src' ) ? '' : '.min'; $this->others[] = "{$baseurl}/tinymce{$mce_suffix}.js?$version"; $this->others[] = "{$baseurl}/plugins/compat3x/plugin{$suffix}.js?$version"; $this->others[] = "{$baseurl}/langs/wp-langs-en.js?$version"; return $mceInit; }}new PoP_ServiceWorkers_Hooks_TinyMCE();
Finally, we add the extracted resources to the precache list:
class PoP_ServiceWorkers_Hooks_TinyMCE { function __construct() { … add_filter('PoP_ServiceWorkers_Job_CacheResources:precache', array($this, 'get_precache_list'), 1000, 2); } … function get_precache_list($precache, $resourceType) { if ($resourceType == 'static') { // In addition, add all the files in the tinymce plugins folder, since these will be needed during runtime when initializing the tinymce textarea $precache = array_merge( $precache, $this->content_css, $this->external_plugins, $this->plugins, $this->others ); } return $precache; }}
We must also precache all images required by the theme and all plugins. In the code below, we precache all of the theme’s files in the folder img/, assuming that these are requested without adding parameters:
class PoPTheme_Wassup_ServiceWorkers_Hooks_ThemeImages { function __construct() { add_filter('PoP_ServiceWorkers_Job_CacheResources:precache', array($this, 'get_precache_list'), 10, 2); } function get_precache_list($precache, $resourceType) { if ($resourceType == 'static') { // Add all the images from the active theme $theme_dir = get_stylesheet_directory(); $theme_uri = get_stylesheet_directory_uri(); foreach (glob($theme_dir."/img/*") as $file) { $precache[] = str_replace($theme_dir, $theme_uri, $file); } } return $precache; }}new PoPTheme_Wassup_ServiceWorkers_Hooks_ThemeImages();
If we use Twitter Bootstrap, loaded from a CDN (for example, https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css), then we must precache the corresponding glyphicons’ font files:
class PoPTheme_Wassup_ServiceWorkers_Hooks_Bootstrap { function __construct() { add_filter('PoP_ServiceWorkers_Job_CacheResources:precache', array($this, 'get_precache_list'), 10, 2); } function get_precache_list($precache, $resourceType) { if ($resourceType == 'static') { // Add all the fonts needed by Bootstrap inside the bootstrap.min.css file $precache[] = 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/fonts/glyphicons-halflings-regular.eot'; $precache[] = 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/fonts/glyphicons-halflings-regular.svg'; $precache[] = 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/fonts/glyphicons-halflings-regular.ttf'; $precache[] = 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff'; $precache[] = 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff2'; } return $precache; }}new PoPTheme_Wassup_ServiceWorkers_Hooks_Bootstrap();
All language-specific resources for all languages must also be precached, so that the website can be loaded in any language when offline. In the code below, we assume the plugin has a js/locales/ folder with the translation files locale-en.js, locale-es.js, etc:
class PoP_UserAvatar_ServiceWorkers_Hooks_Locales { function __construct() { add_filter('PoP_ServiceWorkers_Job_CacheResources:precache', array($this, 'get_precache_list'), 10, 2); } function get_precache_list($precache, $resourceType) { if ($resourceType == 'static') { $dir = dirname(__FILE__)); $url = plugins_url('', __FILE__)); foreach (glob($dir."/js/locales/fileupload/*") as $file) { $precache[] = str_replace($dir, $url, $file); } } return $precache; }}new PoP_UserAvatar_ServiceWorkers_Hooks_Locales();
Non-caching strategies
In the following, we will delve into caching and non-caching strategies. Let’s tackle the non-caching strategy first:
Network only
Whenever JSON requests have user state.
To not cache a request, if we know which URLs must not be cached in advance, then we can simply add their full or partial paths in the list of excluded items. For instance, below, we’ve set all pages that have user state (such as “My posts” and “Edit my profile”) to not be intercepted by the service worker, because we don’t want to cache any personal information of the user:
function get_page_path($page_id) { $page_path = substr(get_permalink($page_id), strlen(home_url())); // Remove the first and last '/' if ($page_path[0] == '/') $page_path = substr($page_path, 1); if ($page_path[strlen($page_path)-1] == '/') $page_path = substr($page_path, 0, strlen($page_path)-1); return $page_path;}class PoP_ServiceWorkers_Hooks_UserState { function __construct() { add_filter('PoP_ServiceWorkers_Job_Fetch:exclude:partial', array($this, 'get_excluded_partialpaths'), 10, 2); } function get_excluded_partialpaths($excluded, $resourceType) { if ($resourceType == 'json') { // Variable $USER_STATE_PAGES contains all IDs of pages that have a user state foreach ($USER_STATE_PAGES as $page_id) { $excluded[] = get_page_path($page_id); } } return $excluded; }}new PoP_ServiceWorkers_Hooks_UserState();
If the non-caching strategy must be applied on runtime, then we can add a parameter, sw-networkonly=true or sw-strategy=networkonly to the requested URL, and dismiss handling it with a service worker in the function shouldHandleFetch:
self.addEventListener('fetch', event => { function shouldHandleFetch(event, opts) { … var params = getParams(url); if (params['sw-strategy'] == 'networkonly') return false; … } …});
File: sw-template.js
Caching strategies
The application uses the following caching strategies, depending on the resource type and functional use:
Cache, falling back to network for the appshell and static resources.
Cache, then network for JSON requests.
Network, falling back to cache for JSON requests that do not keep the user waiting, such as lazy-loaded data (for example, a post’s comments), or that must be up to date (for example, viewing a post after it’s been updated).
Both static and HTML resource types will always require the same strategy. It is only the JSON resource type that can be switched between strategies. We establish the “cache, then network” strategy as the default one, and define rules over the requested URL to switch to “network, falling back to cache”:
startsWith
The URL starts with a predefined full or partial path.
hasParams
The URL contains a predefined parameter. The parameter sw-networkfirst has been defined already, so requesting https://www.mydomain.com/en/?output=json will use the “cache first” strategy, whereas https://www.mydomain.com/en/?output=json&sw-networkfirst=true will switch to “network first.”
// Cache falling back to networkconst SW_CACHEFIRST = 1;// Cache then networkconst SW_CACHEFIRSTTHENREFRESH = 2;// Network falling back to cacheconst SW_NETWORKFIRST = 3;var config = { … strategies: $strategies, …};
Finally, we get to the logic in the service-workers.js file. Please note that to fetch JSON requests, we will also need to add to the URL cache-busting parameter sw-cachebust, with a timestamp to avoid getting the response from the browser’s HTTP cache.
function getCacheBustRequest(request, opts) { var url = new URL(request.url); // Put in a cache-busting parameter to ensure we’re caching a fresh response. if (url.search) { url.search += '&'; } url.search += 'sw-cachebust=' + Date.now(); return new Request(url.toString());}function addToCache(cacheKey, request, response) { if (response.ok) { var copy = response.clone(); caches.open(cacheKey).then( cache => { cache.put(request, copy); }); } return response;}self.addEventListener('fetch', event => { function getStrategy(request, opts) { var strategy = ''; var resourceType = getResourceType(request); // JSON requests have two strategies: cache first + update (the default) or network first if (resourceType === 'json') { var networkFirst = opts.strategies[resourceType].networkFirst; var criteria = { startsWith: networkFirst.startsWith.full.some(path => request.url.startsWith(path)), // The pages do not included the locale domain, so add it before doing the comparison pageStartsWith: networkFirst.startsWith.partial.some(path => request.url.startsWith(opts.locales.domain+path)), // Code for function stripIgnoredUrlParameters is in https://github.com/leoloso/PoP/blob/master/wp-content/plugins/pop-serviceworkers/kernel/serviceworkers/assets/js/jobs/lib/utils.js hasParams: stripIgnoredUrlParameters(request.url, networkFirst.hasParams) != request.url } var successCriteria = Object.keys(criteria).filter(criteriaKey => criteria[criteriaKey]); if (successCriteria.length) { strategy = SW_NETWORKFIRST; } else { strategy = SW_CACHEFIRSTTHENREFRESH; } } else if (resourceType === 'html' || resourceType === 'static') { strategy = SW_CACHEFIRST; } return strategy; } function onFetch(event, opts) { var request = event.request; var resourceType = getResourceType(request); var cacheKey = cacheName(resourceType, opts); var strategy = getStrategy(request, opts); var cacheBustRequest = getCacheBustRequest(request, opts); if (strategy === SW_CACHEFIRST || strategy === SW_CACHEFIRSTTHENREFRESH) { /* Load immediately from the Cache */ event.respondWith( fetchFromCache(request) .catch(() => fetch(request)) .then(response => addToCache(cacheKey, request, response)) ); /* Bring fresh content from the server, and show a message to the user if the cached content is stale */ if (strategy === SW_CACHEFIRSTTHENREFRESH) { event.waitUntil( fetch(cacheBustRequest) .then(response => addToCache(cacheKey, request, response)) .then(response => refresh(request, response)) ); } } else if (strategy === SW_NETWORKFIRST) { event.respondWith( fetch(cacheBustRequest) .then(response => addToCache(cacheKey, request, response)) .catch(() => fetchFromCache(request)) .catch(function(err) {/*console.log(err)*/}) ); } } if (shouldHandleFetch(event, config)) { onFetch(event, config); }});
File: sw-template.js
The “cache then network” strategy uses the refresh function to cache the most updated content coming from the server, and if it differs from the previously cached one, then post a message to the client browser to notify the user. It makes the comparison not of the actual contents, but of their ETag headers (generation of the ETag header will be explained below). The cached ETag value is stored using localForage, a simple yet powerful API wrapping IndexedDB:
function refresh(request, response) { var ETag = response.headers.get('ETag'); if (!ETag) return null; var key = 'ETag-'+response.url; return localforage.getItem(key).then(function(previousETag) { // Compare the ETag of the response with the previous one saved in the IndexedDB if (ETag == previousETag) return null; // Save the new value return localforage.setItem(key, ETag).then(function() { // If there was no previous ETag, then send no notification to the user if (!previousETag) return null; // Send a message to the client return self.clients.matchAll().then(function (clients) { clients.forEach(function (client) { var message = { type: 'refresh', url: response.url }; client.postMessage(JSON.stringify(message)); }); return response; }); }); });}
File: sw-template.js
A JavaScript function catches the messages delivered by the service worker, and prints a message requesting the user to refresh the page:
function showRefreshMsgToUser() { if ('serviceWorker' in navigator) { navigator.serviceWorker.onmessage = function (event) { var message = JSON.parse(event.data); if (message.type === 'refresh') { var msg = 'This page has been updated, <a href="'+message.url+'">click here to refresh it</a>.'; var alert = '<div role="alert"><button type="button" aria-hidden="true" data-dismiss="alert">×</button>'+msg+'</div>'; jQuery('body').prepend(alert); } }; }}
Generating the ETag Header
An ETag header is a hash representing the content being served; because it is a hash, a minimal change in the source will lead to the creation of a completely different ETag. We must make sure that the ETag is generated from the actual website content, and ignore information not visible to the user, such as HTML tag IDs. Otherwise, consider the following sequence happening for the “cache then network” strategy:
An ID is generated, using now() to make it unique, and printed in the page’s HTML.
When accessed for the first time, this page is created and its ETag is generated.
When accessed a second time, the page is served immediately from the service worker cache, and a network request is triggered to refresh the content.
This request generates the page again. Even if it hasn’t been updated, its content will be different, because now() will produce a different value, and its ETag header will be different.
The browser will compare the two ETags and, because they are different, prompt the user to refresh the content, even if the page has not been updated.
One solution is to remove all dynamically generated values, such as current_time('timestamp') and now(), before generating the ETag. For this, we can set all dynamic values in constants, and then use these constants throughout the application. Finally, we would remove these from the input to the hash-generation function:
define('TIMESTAMP', current_time('timestamp'));define('NOW', now());ob_start();// All the application code in between (using constants TIMESTAMP, NOW if needed)$content = ob_get_clean();$dynamic = array(TIMESTAMP, NOW);$etag_content = str_replace($dynamic, '', $content);header('ETag: '.wp_hash($etag_content));echo($content);
A similar strategy is needed for those pieces of information that are allowed to become stale, such as a post’s comments count, mentioned earlier. Because this value is not important, we don’t want the user to receive a notification to refresh the page merely because the number of comments has increased from 5 to 6.
Appshell With Support for Multilingual, Multiple Presentation Modes
No matter which URL is requested by the user, the application will load the appshell instead, which will immediately load the contents of the requested URL (still accessible in window.location.href) through the API, passing along the locale and all needed parameters.
The application has different views and languages, and we want these different appshells to be precached, and then load the appropriate one on runtime, fetching the information from the requested URL: https://www.mydomain.com/language-code/path/to/page/?view=….
As mentioned earlier, given two languages (English and Spanish) and three views (default, embed and print), we will need to precache the following appshells:
In addition to the language and the view, the application might have other parameters (let’s say “style” and “format”). However, adding these would make the combinations of URLs to precache grow tremendously. So, we need to settle on a trade-off, deciding which parameters to precache (the most used ones) and which ones not to. For the latter ones, their corresponding URL can be accessed offline only starting from the second access.
By adding hooks into the configuration, we allow multilingual plugins, such as qTranslate X, to modify the locales and languages and the URLs accordingly.
function get_sw_configuration() { … $configuration['$appshellPages'] = get_sw_appshell_pages(); $configuration['$appshellParams'] = apply_filters('PoP_ServiceWorkers_Job_Fetch:appshell_params', array("themestyle", "settingsformat", "mangled")); …}function get_sw_appshell_pages() { // Locales: can be hooked into by qTranslate to input the language codes $locales = apply_filters('PoP_ServiceWorkers_Job_Fetch:locales', array(get_locale())); $views = array("default", "embed", "print"); $appshellPages = array(); foreach ($locales as $locale) { foreach ($views as $view) { // By adding a hook to the URL, we can allow plugins to modify the URL $appshellPages[$locale][$view] = apply_filters('PoP_ServiceWorkers_Job_Fetch:appshell_url', add_query_arg('view', $view, get_permalink($APPSHELL_PAGE_ID), $locale); } } return apply_filters('PoP_ServiceWorkers_Job_Fetch:appshell_pages', $appshellPages);}
File: sw.php
class PoP_ServiceWorkers_QtransX_Job_Fetch_Hooks { function __construct() { add_filter('PoP_ServiceWorkers_Job_Fetch:locales', array($this, 'get_locales')); add_filter('PoP_ServiceWorkers_Job_Fetch:appshell_url', array($this, 'get_appshell_url'), 10, 2); … } function get_locales($locales) { global $q_config; if ($languages = $q_config['enabled_languages']) { return $languages; } return $locales; } function get_appshell_url($url, $lang) { return qtranxf_convertURL($url, $lang); }}new PoP_ServiceWorkers_QtransX_Job_Fetch_Hooks();
After merging sw-template.js into service-workers.js, it will look like this:
The request is intercepted with the method onFetch, and if it is of the HTML resource type, it will be replaced with the appshell URL instead, just after deciding which strategy is to be employed. (Below we’ll see how to get the current locale, set in opts.locales.current.)
function onFetch(event, opts) { var request = event.request; … var strategy = getStrategy(request, opts); // Allow to modify the request, fetching content from a different URL request = getRequest(request, opts); …}function getRequest(request, opts) { var resourceType = getResourceType(request); if (resourceType === 'html') { // The different appshells are a combination of locale and view. var params = getParams(request.url); var view = params.view || 'default'; // The initial appshell URL has the params that we have precached. var url = opts.appshell.pages[opts.locales.current][view]; // In addition, there are other params that, if provided by the user, must be added to the URL. These params are not originally precached in any appshell URL, so such a page will have to be retrieved from the server. opts.appshell.params.forEach(function(param) { // If the param was passed in the URL, then add it along. if (params[param]) { url += '&'+param+'='+params[param]; } }); request = new Request(url); } return request;}
Lastly, we proceed to precache the appshells:
class PoP_ServiceWorkers_Hooks_AppShell { function __construct() { add_filter('PoP_ServiceWorkers_Job_CacheResources:precache', array($this, 'get_precache_list'), 10, 2); } function get_precache_list($precache, $resourceType) { if ($resourceType == 'html') { foreach (get_sw_appshell_pages() as $locale => $views) { foreach ($views as $view => $url) { $precache[] = $url; } } } return $precache; }}new PoP_ServiceWorkers_Hooks_AppShell();
Obtaining the locale
The appshell has multilingual support, so we need to extract the language information from the requested URL.
Finally, we obtain the language code from the requested URL, and initialize the locale’s current and domain configuration values at the beginning of the fetch event:
self.addEventListener('fetch', event => { config = initOpts(config, event); if (shouldHandleFetch(event, config)) { … }});function initOpts(opts, event) { // Find the current locale and set it on the configuration object opts.locales.current = getLocale(event, opts); opts.locales.domain = getLocaleDomain(event, opts); return opts;}function getLocale(event, opts) { var currentDomain = getCurrentDomain(event, opts); if (currentDomain.length) { return opts.locales.all[currentDomain]; } return opts.locales.default;}function getLocaleDomain(event, opts) { var currentDomain = getCurrentDomain(event, opts); if (currentDomain.length) { return currentDomain[0]; } // Return the default domain return Object.keys(opts.locales.all).filter(function(key) {return opts.locales.all[key] === opts.locales.default})[0];}function getCurrentDomain(event, opts) { return Object.keys(opts.locales.all).filter(path => event.request.url.startsWith(path));}
Dealing With Nonces
A nonce (or a “number used once”) is a cryptographic hash used to verify the authenticity of a person or client. WordPress uses nonces as security tokens to protect URLs and forms from malicious attacks. In spite of their name, WordPress uses a nonce more than once, giving it a limited lifetime, after which it expires. Even though they are not the ultimate security measure, nonces are a good first filter to prevent hacker attacks.
The HTML code printed on any WordPress page will contain nonces, such as the nonce for uploading images to the media manager, saved in the JavaScript object _wpPluploadSettings.defaults.multipart_params._wpnonce. The lifetime of a nonce is, by default, set to 24 hours (configured in the nonce_life hook). However, this value is shorter than the expected duration of the appshell in the service worker cache. This is a problem: After just 24 hours, the appshell will contain invalid nonces, which will make the application malfunction, such as giving error messages when the user attempts to upload images.
There are a few solutions to overcome this problem:
Immediately after loading the appshell, load another page in the background, using the “network only” strategy, to update the value of the nonce in the original JavaScript object:
Implement a longer nonce_life, such as three months, and then make sure to deploy a new version of the service worker within this lifespan:
add_filter('nonce_life', 'sw_nonce_life'); function sw_nonce_life($nonce_life) { return 90*DAY_IN_SECONDS; }
Because this solution weakens the security of nonces, tougher security measures must also be put in place throughout the application, such as making sure that the user can edit a post:
if (!current_user_can('edit_post', $post_id)) wp_die( __( 'Sorry, you are not allowed to edit this item.'));
Additional Considerations
The fact that something can be done doesn’t mean it should be done. The developer must evaluate whether each feature needs to be added according to the requirements of the app, not just because the technology allows it.
Below, I’ll show a few examples of functionality that can be perfectly implemented using other solutions or that need some workaround in order to make them work with service workers.
Showing a simple “You’re offline” message
Service workers can be used to display an offline fallback page whenever the user has no Internet connection. In its most basic implementation of just showing a “You are offline” message, I believe that we are not getting the most value out of this fallback page. Rather, it could do more interesting things:
Provide additional information, such as showing a list of all already-cached resources, which the user can still browse offline (check how Jake Archibald’s offline Wikipedia demo lists all already-cached resources on its home page).
Let the user play a game while waiting for the connection to come back (such as done by The Guardian).
With an SPA, we can offer a different approach: We can intercept the offline state, and just display a small “You’re offline” message at the top of the page that the user is currently browsing. This avoids redirection of the user to yet another page, which could impair the flow of the application.
function intercept_click() { $(document).on('click', 'a[href^="'+WEBSITE_DOMAIN+'"]', function(e) { var anchor = $(this); var url = anchor.attr('href'); $.ajax({ url: url, error: function(jqXHR) { var msg = 'Oops, there was an error'; if (jqXHR.status === 0) { // status = 0 => user is offline msg = 'You are offline!'; } else if (jqXHR.status === 404) { msg = 'That page doesn't exist'; } $('#error-msg').text(msg).css('display', 'block'); } }); });}
Using localStorage to cache data
Service workers are not the only solution offered by browsers for caching the response’s data. An older technology, with even wider support (Internet Explorer and Safari support it), is localStorage. It offers good performance for caching small to medium-sized pieces of information (it can normally cache up to 5 MB of data).
/* Using Modernizr library */function intercept_click() { $(document).on('click', 'a[href^="'+WEBSITE_DOMAIN+'"]', function(e) { var anchor = $(this); var url = anchor.attr('href'); var stored = ''; if (Modernizr.localstorage) { stored = localStorage[url]; } if (stored) { // We already have the data! process(stored); } else { $.ajax({ url: url, success: function(response){ // Save the data in the localStorage if (Modernizr.localstorage) { localStorage[url] = response; } process(response); } }); } });}
Making things prettier
To force the service worker to employ the “network first” strategy, we can add an extra parameter, sw-networkfirst=true, to the requested URL. However, adding this parameter in the actual link would look ugly (details of technical implementation should be hidden from the user, as much as possible).
Instead, a data attribute, data-sw-networkfirst, could be added in the anchor. Then, on runtime, the click by the user would be intercepted to be handled by an AJAX call, checking whether the link clicked has this data attribute; if it does, only then will the parameter sw-networkfirst=true be added to the URL to fetch:
function intercept_click() { $(document).on('click', 'a[href^="'+WEBSITE_DOMAIN+'"]', function(e) { var anchor = $(this); var url = anchor.attr('href'); if (anchor.data('sw-networkfirst')) { url = add_query_arg('sw-networkfirst', 'true', url); } $.ajax({ url: url, … }); });}
Planning for things that do not work
Not everything will work offline. For instance, a CAPTCHA will not work because it needs to synchronize its value with the server. If a form has a CAPTCHA, then attempting to submit the form while offline should not save the value locally, to be sent once the Internet connection comes back. Instead, it could fill the form once again with all values previously filled by the user, request the user to input the CAPTCHA, and only then submit the form.
Conclusion
We’ve seen how service workers can be implemented for a WordPress website with an SPA architecture. SPAs greatly enhance service workers, such as enabling you to choose from different appshells to load during runtime. Integrating with WordPress is not all that smooth, at least to make the website become offline first, because we need to find all resources from the theme and all of the plugins to add to the precache list. However lengthy, though, the integration is worth doing: The website will load faster and will work offline.
Dapulse is the next generation of visual tools, built specifically for designers and developers. See what everyone on your team is working on in a single glance.
Today we’d like to share an interesting distortion effect with you. The main concept of this demo is to use a displacement map in order to distort an underlying image, giving it different types of effects. To demonstrate the liquid-like transitions between images, we’ve created a slideshow.
What a displacement map generally does, is using an image as a texture, that is later applied to an object, giving the illusion that the underlying object is wrapped around that texture. This is a technique commonly used in many different areas, but today we’ll explore how this can be applied to a simple image slideshow.
We’ll be using PixiJS as our renderer and filtering engine and GSAP for our animations.
Getting Started
In order to have a displacement effect, you need a displacement map texture. In this demo’s code we’ve provided with different types of textures you can use, but of course you can create one of your own, for example by using Photoshop’s render tool. Keep in mind that this image’s dimensions affect the end result, so playing around with differently sized textures, might give you different effect looks.
A general rule of thumb is that your texture image should be a power of 2 sized texture. What this means is that its width and height can be doubled-up or divided-down by 2. This ensures that your texture is optimized to run fast, without consuming too much memory. In other words the suggested dimensions for your texture image (width and/or height), would be: 8, 16, 32, 64, 128, 256, 512, 1024, 2048 etc.
For the demos, we’ve created a slideshow that, when navigating, shows the effect as a transition on the slides. We’ll also add some other options, but we’ll just go through the main idea of the distortion effect.
Markup
Our base markup for this demo is really minimal. We just need the navigation buttons for our slider and a wrapper for the slides. We use this wrapper to pass our slides to our component, therefore we hide it by default with CSS. This markup to JS approach may simplify the task of adding images to the slideshow, when working in a more dynamic environment. However, if it suits you better, you could just easily pass them as an array, upon initializing.
The idea is fairly simple: we add all of our slides into a container, apply the displacement filter and render. Then, when clicking the navigation buttons, we set the alpha property of the current image to 0, set the next one’s to 1 and tweak the displacement filter while navigating.
var renderer = new PIXI.autoDetectRenderer(); var stage = new PIXI.Container(); var slidesContainer = new PIXI.Container(); var displacementSprite = new PIXI.Sprite.fromImage( displacementImage ); var displacementFilter = new PIXI.filters.DisplacementFilter( displacementSprite ); // Add canvas to the HTML document.body.appendChild( renderer.view ); // Add child container to the stage stage.addChild( slidesContainer ); // Set the filter to stage stage.filters = [displacementFilter]; // We load the sprites to the slides container and position them at the center of the stage // The sprites array is passed to our component upon its initialization // If our slide has text, we add it as a child to the image and center it function loadPixiSprites( sprites ) { for ( var i = 0; i < sprites.length; i++ ) { var texture = new PIXI.Texture.fromImage( sprites[i] ); var image = new PIXI.Sprite( texture ); if ( texts ) { // Base styles for our Text var textStyle = new PIXI.TextStyle({ fill: '#ffffff', wordWrap: true, wordWrapWidth: 400 }); var text = new PIXI.Text( texts[i], textStyle); image.addChild( text ); // Center each to text to the image text.anchor.set(0.5); text.x = image.width / 2; text.y = image.height / 2; } image.anchor.set(0.5); image.x = renderer.width / 2; image.y = renderer.height / 2; slidesContainer.addChild( image ); } };
That would be the most basic setup you’d need in order for the scene to be ready. Next thing we want to do is handle the clicks of the navigation buttons. Like we said, when the user clicks on the next or previous button, we change the alpha property of the according slide and tweak our Displacement Filter. We use a simple timeline for this, which you could of course customize accordingly.
// We listen at each navigation element click and call the move slider function // passing it the index we want to go to var currentIndex = 0; var slideImages = slidesContainer.children; var isPlaying = false; for ( var i = 0; i < nav.length; i++ ) { var navItem = nav[i]; navItem.onclick = function( event ) { // Make sure the previous transition has ended if ( isPlaying ) { return false; } if ( this.getAttribute('data-nav') === 'next' ) { if ( that.currentIndex >= 0 && that.currentIndex < slideImages.length - 1 ) { moveSlider( currentIndex + 1 ); } else { moveSlider( 0 ); } } else { if ( that.currentIndex > 0 && that.currentIndex < slideImages.length ) { moveSlider( currentIndex - 1 ); } else { moveSlider( spriteImages.length - 1 ); } } return false; } } // Our transition between the slides // On our timeline we set the alpha property of the relevant slide to 0 or 1 // and scale out filter on the x & y axis accordingly function moveSlider( newIndex ) { isPlaying = true; var baseTimeline = new TimelineMax( { onComplete: function () { that.currentIndex = newIndex; isPlaying = false; }}); baseTimeline .to(displacementFilter.scale, 1, { x: 200, y: 200 }) .to(slideImages[that.currentIndex], 0.5, { alpha: 0 }) .to(slideImages[newIndex], 0.5, { alpha: 1 }) .to(displacementFilter.scale, 1, { x: 20, y: 20 } ); };
Finally, we have to render our scene and optionally add some default animations.
// Use Pixi's Ticker class to render our scene // similar to requestAnimationFrame var ticker = new PIXI.ticker.Ticker(); ticker.add( function( delta ) { // Optionally have a default animation displacementSprite.x += 10 * delta; displacementSprite.y += 3 * delta; // Render our stage renderer.render( stage ); });
Working Demo
This should sum up the most basic parts of how the demo works and give you a good starting point if you want to edit it according to your needs. However, if you don’t want to mess with too much code and need a quick working demo to play on your own, there are several options you could use when you initialize the component. So just include the script on your page and add the following code wherever you want to show your slideshow. Play around with different values to get started and don’t forget to try out different displacement map textures for different effects.
// Select all your images var spriteImages = document.querySelectorAll( '.slide-item__image' ); var spriteImagesSrc = []; var texts = []; for ( var i = 0; i < spriteImages.length; i++ ) { var img = spriteImages[i]; // Set the texts you want to display to each slide // in a sibling element of your image and edit accordingly if ( img.nextElementSibling ) { texts.push(img.nextElementSibling.innerHTML); } else { texts.push(''); } spriteImagesSrc.push( img.getAttribute('src' ) ); } // Initialise the Slideshow var initCanvasSlideshow = new CanvasSlideshow({ // pass the images you want as an array sprites: spriteImagesSrc, // if you want your slides to have title texts, pass them as an array texts: texts, // set your displacement texture displacementImage: 'https://imgur.com/a/Ea3wo', // optionally start with a default animation autoPlay: true, // [x, y] controls the speed for your default animation autoPlaySpeed: [10, 3], // [x, y] controls the effect amount during transitions displaceScale: [200, 70], // choose whether or not you slideshow will take up all the space of the viewport fullScreen: true, // If you choose to not have a fullscreen slideshow, set the stage's width & height accordingly stageWidth: 800, stageHeight: 600, // add you navigation element. Should have a 'data-nav' attribute with a value of next/previous navElement: document.querySelectorAll( '.scene-nav' ), // will fit the filter bounding box to the renderer displaceAutoFit: false });
Interactive
Last thing we want to do is optionally make our stage interactive. That is instead of auto playing, have our effect interact with our mouse. Just set the the interactive property to be true and play around with your mouse.
var initCanvasSlideshow = new CanvasSlideshow({ interactive: true ... });
In all mouse interactions we listen for the corresponding event, and based on the event data, we scale our displacement event respectively. It looks like this:
Editor’s Note: We’ve been closely working with Maya on this article, and we’re happy to see the final result now being published on 18F. We highly encourage more teams to share the lessons they learned when building design systems or pattern libraries, and we’re always happy to support them in writing, editing and shaping that article. This post is a re-post of Maya’s final article.
Today, there are nearly 30,000 U.S federal websites with almost no consistency between them. Between the hundreds of thousands of government employees working in technology, there’s nothing in common with how these websites are built or maintained.
As a result, the government is spending considerable resources on services that aren’t meeting the needs of their users. Federal websites are the front door to government services: it’s the first thing someone encounters when interacting with the government. According to research from the Federal Front Door initiative, as government interfaces erode, so does the public’s trust in those services.
I was part of a team of designers and developers who unified a complex system with numerous rules to serve users from all corners of the country. I’ll shed some light on how we built tools to leverage industry-standard best practices and produce a design system with reusable components. You’ll also see how our system is helping agency teams in the federal government create simple, efficient, consistent experiences quickly and at reduced cost.
The Problem: Inconsistent User Experiences Across Government Websites
When the American people go online to access government services, they’re often met with confusing navigation systems, an array of visual brands, and inconsistent interaction patterns. Websites intended to help people access information and services, like a veteran looking for help to go back to college, are splintered off of various agencies and organizations.
For example, consider what it’s like for a young veteran looking to apply for federal student loans to help her cover the cost of attending college. Let’s call this person Joanne. Joanne had to wade through multiple agency websites to access the federal programs that could help her afford college. Joanne was confused. She was overwhelmed by how hard these tools were to use, missed opportunities she was eligible for, and felt frustrated and isolated. The system that was supposed to help her stood in her way. Creating consistency between these systems will help people (like Joanne) more effectively access the services they need and increase their trust in the government.
Why It’s Like This: Limitations To Consistent User Experiences In Government
Dedicated federal workers want to build helpful digital tools for everyone. They want to be able to develop quick prototypes and sites. They choose resources with minimal documentation that allow them to get up and running quickly.
Other one-off designers or front-end developers in an agency are trying to do the right thing but without a lot of time or support. They need tools to cut down on design and development time, and a way to advocate for best practices to higher ups.
Therefore, the question in front of us became:
Could we create a shared set of tools to provide consistent, effective, and easy-to-use government websites?
We think the answer is yes.
The Team
In the summer of 2015, a team from 18F and the U.S. Digital Services formed to work on these tools. We asked ourselves: how do we bring together thousands of public websites into a common design language?
To answer this question, twenty designers and developers working on digital services in government gathered in Washington, DC to work on this problem.
The first question we asked ourselves was: what are the components and patterns we’re looking for in a pattern library? What are the elements that could help us build a library of patterns and systems of styles? We wrote down all the parts that make up our websites and what we would want in a system. We stuck these ideas on a wall and grouped them together to find what was universal across our systems. We then looked for patterns, taking note of what were the most common. Some of the simplest things kept coming up again and again: color, typography, grids, and buttons.
During our meetings, the team mentioned other components. For instance, people also asked about unique components like data visualizations and calendar widgets. However, by limiting components to the basic building blocks, we could get it in the hands of designers and developers as quickly as possible and see for ourselves what was clicking and what wasn’t.
Building a library to create consistency is similar to playing with Lego bricks as opposed to say mud. When you give people a handful of mud and tell them to build a house, each house will look different: a little lopsided and squishy. When you give those same people five kinds of Lego bricks, they can create a million different houses. Each house looks consistent, not uniform.
Building The System
We started to explore how we could bring consistency across interactions, user experiences, and behavior across those websites. Joanne wants to understand she’s on a government site. She wants it to feel familiar and be intuitive, so she knows what to do and can accomplish her task. A consistent look and feel with common design elements will feel familiar, trustworthy, and secure — and people like Joanne will be able to navigate government websites more easily because of a common palette and design.
Interface inventory
We used analytics.usa.gov to look at the top visited .gov domains to surface common colors and component styles. We wondered: “Do we need 32 different shades of blue?” We were surprised by so many different button styles on government website. Do we really need 64 types of buttons? Surfacing and categorizing components across government websites allowed us to see the inconsistencies between government websites as well as what components they had in common.
The interface inventory and results from our workshop were combined and prioritized with the help of government designers. Once we had our list of components to start with, our user researchers began researching, creating wireframes, and conducting user testing of the components and design system website.
The user experience team members researched, created wireframes, and tested components like this sign-in form. Visual designers created higher fidelity designs based on the wireframes, which were later developed in code.
Mood boarding
Our visual designers began to explore what it would look and feel like. We knew we wanted the system to feel simple, modern, accessible, approachable, and trustworthy. They created three mood boards, which looked at various typography and color samples as well as inspirational design imagery.
The three styles we looked at were:
Clean and Classic
Inspiring and Empowering
Modern American
Our team’s designers worked with visual designers across government and conducted a dot-voting exercise surfacing what they liked about each mood board. We put these three directions up on GitHub to solicit feedback from a range of government digital service employees, where we could fine-tune the direction. In the end, people liked the bold, saturated colors of Modern American and the typography of Clean and Classic, which incorporated a sans-serif font and a serif typeface.
Typography
Once the style was defined, our visual designers started to explore which typefaces to use. We needed to find a font that was legible, communicated trust and credibility, and was open source. Since paid fonts would have created additional burdens around licensing, we needed to find fonts that were free and open source to make it easy for government designers to use the font.
To promote legibility, we looked at fonts that had a large x-height, open counters, and a range of font weights. In order to provide the greatest flexibility for government designers, we wanted to find a sans-serif font for its clean, modern aesthetic that’s highly legible on interfaces and a serif font, for a traditional look that could be used for text-dense content or added contrast between headings.
Our visual designers tested typography pairings by replacing fonts on actual government websites with these choices to find the fonts that would meet these needs. By omitting the name of the typeface, designers weren’t influenced by what font it was and could focus on how it read. Then we tested these fonts with government designers to identify which font was the most legible and matched our desired aesthetic. In the end, the fonts we chose were Source Sans Pro and Merriweather.
Source Sans Pro is an open-source sans serif typeface created for legibility in UI design. With a variety of weights that read easily at all sizes, Source Sans Pro provides clear headers as well as highly readable body text. Inspired by twentieth-century American gothic typeface design, its slender but open letters offer a clean and friendly simplicity.
Merriweather is an open-source serif typeface designed for on-screen reading. This font is ideal for text-dense design: the letterforms have a tall x-height but remain relatively small, making for excellent readability across screen sizes while not occupying extra horizontal space. The combination of slim and thick weights gives the font family stylistic range, while conveying a desirable mix of classic, yet modern simplicity. Merriweather communicates warmth and credibility at both large and smaller font sizes.
From a technical standpoint, we needed to ensure the fonts we provide would perform quickly for users. While our visual designers wanted an array of weights, our developers reminded everyone that this would create a burden on users that have to load extra font families. To compromise, we created different font pairings: a robust option with more font weights and a trimmed down version for quicker load times. Armed with this knowledge, government designers can weigh the options themselves to find which would suit their design and performance needs.
Colors
The repeated use of colors found in the interface inventory of government websites informed our color palette. A simple, minimalist palette of cool blue and gray provides a neutral backdrop for brighter shades of red and blue to contrast against. This creates a clean and engaging palette, leaving people feeling like they’re in good hands. The colors are divided by primary, secondary, background, and tertiary colors.
Primary colors are blue, gray, and white. Blue weaves through buttons, links, and headings to bring a sense of calmness, trust, and sincerity through the interface. Clean white content areas allow the typography to “pop” on the page.
Secondary colors are the accent colors of bright blue and red are used sparingly on specialized elements to add lightness and a modern flair. They may be used to highlight important features on a page, like a call to action, or for visual design elements like illustrations.
Background colors are shades of gray used for background blocks of large content areas.
Tertiary colors are used for content-specific needs and are used sparingly, such as in alerts or illustrations.
<
The range of colors in the palette can be flexibly applied to support a range of distinct visual styles. For example, by abstracting color names, such as primary and secondary, we can support agencies that need to conform the palette to their unique brand’s needs. A change once in the color value spreads throughout the system across buttons, accents, and headings.
Because government sites must be accessible to anyone with a disability, in conformance with Section 508, the Standards ensures there is enough contrast between text and its background color. Following WCAG 2.0 guidelines, the Standards provide combinations where the contrast between the text and background is greater than or equal to 4.5:1.
By using bright saturated tints of blue and red, grounded in sophisticated deeper shades of blues and grays, we could communicate warmth and trustworthiness, support a range of distinct visual styles, and meet the highest accessibility standards and color contrast requirements.
Space
The last piece in the building blocks of the design system is how these elements flow in space and provides structure. We provide balanced spacing throughout the type system by placing adequate margins above and below heading elements and paragraph text. By using em’s or relative units, white space is proportionate to the font size and automatically distributes the correct ratio throughout the system. If an agency needs to change a font size, spacing will automatically adjust.
To hold the structure of the content, we provide a 12-column grid system using Neat, a flexible and lightweight Sass grid by thoughtbot. We provide an easy-to-use grid system comprised of a grid container to contain the content centered in the page and sections of halves, thirds, quarters, sixths, and twelfths to lay out content. Simple classes, like usa-grid and usa-width-one-half allow developers to quickly mock up page layouts. We provide three breakpoints, which allows the grid to reflow at smaller sizes, and people may always fine tune the breakpoints to suit their content. A flexible grid system allows visitors to quickly read the page.
Typography, colors, and space form the foundation of the design system, which is used to build components like buttons, forms, and navigation.
Complicated Tasks, Ambitious Goals
The U.S. Web Design Standards launched in September 2015 as a visual style guide and UI component library with the goal of bringing a common design language for government websites all under one hood. In the two years since we were tasked to unify the design and look of all U.S. government websites, over 100 government projects have adopted the standards, helping it evolve, reshape, and move forward in ways we couldn’t imagine. From the Department of Veterans’ Affairs to the U.S. Department of Agriculture, government teams are coming together to set a new bar for federal government websites. In this short time, we’ve begun seeing consistency and better user experiences across government websites. While the goal was to unify a government design language, the unique expression of it has been multifaceted and boundless. And just like building a house out of Lego blocks, expression within the meaningful constraints of a modular design system creates diverse products that are consistent, not uniform.
By providing designers and developers with easy-to-use tools to deliver the highest quality government websites to the American people, the design system is helping create connections across disciplines and move government designers and developers forward — user research, human-centered design, visual design, front-end, and accessibility best practices all come together.
Lessons Learned: Drafting Your Own Standards Within Your Company
Whether you’re a small company or one of the largest governments in the world, you can create your own standards to solve your unique needs. Every pattern library should be different because it should serve the specific needs of the group creating them.
Talk to the people: You’ll need to find out where the problems are and whether or not these problems can be solved by design patterns. Find out if there are common needs across groups. What aspects of what you’re building are required for you to do your job?
Look for duplication of efforts: Where are you repeating yourselves? Where are you wasting time? What takes the longest or is the most challenging when building out websites? Where does friction arise?
Know your values: What your design system will end up looking like will also depend on what’s important to you. What are your values? What principles can guide how you build things?
Empower your team: You need a dedicated group charged with working on this and support from leaders to give you the air cover to do this work. It should be as important as any other project. You’ll need a multidisciplinary team with expertise from user experience research and design, visual design, and front-end development. You’ll need someone to fulfill the role of project manager and product owner to guide the project forward toward the right goals.
Start small and iterate: Figure out what your core needs are, build those out, test them, and listen to what people are asking for. That’s how you’ll find out what is missing. Starting with a limited set of components will save time and give you real answers right away when you start putting it out in the world and in people’s hands.
Don’t work in a vacuum: You’ll need to build consensus, understand what people need, and learn how they build websites, so find people that will use the system. Let that guide your decisions. While you may be more isolated getting the initial system setup, get it out there so you can begin testing and learning. As you build out products with your system and test them with real users, you’ll have the information you need to keep making improvements.
Reuse and specialize: It’s great to see how others have solved problems, and reuse when you can, but know that their solutions are solving their problems. Your problems may need a unique approach. Don’t fall into the trap of “this is what a pattern library should look like” just because someone else is doing it that way.
Promote your system: Get people excited about what you’re doing by talking about the value they’ll get for free by using it: consistent, beautiful, user friendly design with accessible interfaces that will save them time and money.
Be flexible: People don’t like things that are forced on them. Give them opportunities to learn about it and ask questions. Give them permission to make it their own.
Conclusion
When building out a large-scale design system, it can be hard to know where to start. By focusing on the basics, from core styles to coding conventions to design principles, you can create a strong foundation that spreads to different parts of your team. These building blocks can be stacked in many ways to support a multitude of needs and use cases. By building flexibility into the system, users can easily adapt the patterns and designs to support the diverse scope and needs of digital services. Signaling that things can be customized invites people to participate in the system and make it their own. Only when people have a stake in the system will they feel invested to use it and contribute back, making it more robust, versatile, and able to stand the test of time.
It takes a lot of blocks and a lot of time to build these kinds of large design systems, and it’s important to keep people like Joanne in mind. The people on the other side who are scrolling through your designs, clicking your buttons, and filling out your forms so they can access the critical services they need. A solid, usable design system can make all the difference to people like Joanne.
Editor’s Note: We’ve been closely working with Maya on this article, and we’re happy to see the final result now being published on 18F1. We highly encourage more teams to share the lessons they learned when building design systems2 or pattern libraries3, and we’re always happy to support them in writing, editing and shaping that article. This post is a re-post of Maya’s final article.
Today, there are nearly 30,000 U.S federal websites4 with almost no consistency between them. Between the hundreds of thousands of government employees working in technology, there’s nothing in common with how these websites are built or maintained.
As a result, the government is spending considerable resources on services that aren’t meeting the needs of their users5. Federal websites are the front door to government services: it’s the first thing someone encounters when interacting with the government. According to research from the Federal Front Door6 initiative, as government interfaces erode, so does the public’s trust in those services.
I was part of a team of designers and developers who unified a complex system with numerous rules to serve users from all corners of the country. I’ll shed some light on how we built tools to leverage industry-standard best practices and produce a design system with reusable components. You’ll also see how our system is helping agency teams in the federal government create simple, efficient, consistent experiences quickly and at reduced cost.
The Problem: Inconsistent User Experiences Across Government Websites Link
When the American people go online to access government services, they’re often met with confusing navigation systems, an array of visual brands, and inconsistent interaction patterns. Websites intended to help people access information and services, like a veteran looking for help to go back to college, are splintered off of various agencies and organizations.
For example, consider what it’s like for a young veteran looking to apply for federal student loans to help her cover the cost of attending college. Let’s call this person Joanne. Joanne had to wade through multiple agency websites to access the federal programs that could help her afford college. Joanne was confused. She was overwhelmed by how hard these tools were to use, missed opportunities she was eligible for, and felt frustrated and isolated. The system that was supposed to help her stood in her way. Creating consistency between these systems will help people (like Joanne) more effectively access the services they need and increase their trust in the government.
Why It’s Like This: Limitations To Consistent User Experiences In Government Link
Dedicated federal workers want to build helpful digital tools for everyone. They want to be able to develop quick prototypes and sites. They choose resources with minimal documentation that allow them to get up and running quickly.
Other one-off designers or front-end developers in an agency are trying to do the right thing but without a lot of time or support. They need tools to cut down on design and development time, and a way to advocate for best practices to higher ups.
Therefore, the question in front of us became:
Could we create a shared set of tools to provide consistent, effective, and easy-to-use government websites?
In the summer of 2015, a team from 18F and the U.S. Digital Services formed to work on these tools. We asked ourselves: how do we bring together thousands of public websites into a common design language?
To answer this question, twenty designers and developers working on digital services in government gathered in Washington, DC to work on this problem.
The first question we asked ourselves was: what are the components and patterns we’re looking for in a pattern library? What are the elements that could help us build a library of patterns and systems of styles? We wrote down all the parts that make up our websites and what we would want in a system. We stuck these ideas on a wall and grouped them together to find what was universal across our systems. We then looked for patterns, taking note of what were the most common. Some of the simplest things kept coming up again and again: color, typography, grids, and buttons.
During our meetings, the team mentioned other components. For instance, people also asked about unique components like data visualizations and calendar widgets. However, by limiting components to the basic building blocks, we could get it in the hands of designers and developers as quickly as possible and see for ourselves what was clicking and what wasn’t.
Building a library to create consistency is similar to playing with Lego bricks as opposed to say mud. When you give people a handful of mud and tell them to build a house, each house will look different: a little lopsided and squishy. When you give those same people five kinds of Lego bricks, they can create a million different houses. Each house looks consistent, not uniform.
We started to explore how we could bring consistency across interactions, user experiences, and behavior across those websites. Joanne wants to understand she’s on a government site. She wants it to feel familiar and be intuitive, so she knows what to do and can accomplish her task. A consistent look and feel with common design elements will feel familiar, trustworthy, and secure — and people like Joanne will be able to navigate government websites more easily because of a common palette and design.
We used analytics.usa.gov22 to look at the top visited .gov domains to surface common colors and component styles. We wondered: “Do we need 32 different shades of blue?” We were surprised by so many different button styles on government website. Do we really need 64 types of buttons? Surfacing and categorizing components across government websites allowed us to see the inconsistencies between government websites as well as what components they had in common.
The interface inventory and results from our workshop were combined and prioritized with the help of government designers. Once we had our list of components to start with, our user researchers began researching, creating wireframes, and conducting user testing of the components and design system website.
The user experience team members researched, created wireframes, and tested components like this sign-in form. Visual designers created higher fidelity designs based on the wireframes, which were later developed in code.
Our visual designers began to explore what it would look and feel like. We knew we wanted the system to feel simple, modern, accessible, approachable, and trustworthy. They created three mood boards, which looked at various typography and color samples as well as inspirational design imagery.
The three styles we looked at were:
Clean and Classic
Inspiring and Empowering
Modern American
Our team’s designers worked with visual designers across government and conducted a dot-voting exercise29 surfacing what they liked about each mood board. We put these three directions up on GitHub30 to solicit feedback from a range of government digital service employees, where we could fine-tune the direction. In the end, people liked the bold, saturated colors of Modern American and the typography of Clean and Classic, which incorporated a sans-serif font and a serif typeface.
Once the style was defined, our visual designers started to explore which typefaces to use. We needed to find a font that was legible, communicated trust and credibility, and was open source. Since paid fonts would have created additional burdens around licensing, we needed to find fonts that were free and open source to make it easy for government designers to use the font.
To promote legibility, we looked at fonts that had a large x-height, open counters, and a range of font weights. In order to provide the greatest flexibility for government designers, we wanted to find a sans-serif font for its clean, modern aesthetic that’s highly legible on interfaces and a serif font, for a traditional look that could be used for text-dense content or added contrast between headings.
Our visual designers tested typography pairings by replacing fonts on actual government websites with these choices to find the fonts that would meet these needs. By omitting the name of the typeface, designers weren’t influenced by what font it was and could focus on how it read. Then we tested these fonts with government designers to identify which font was the most legible and matched our desired aesthetic. In the end, the fonts we chose were Source Sans Pro and Merriweather.
Source Sans Pro is an open-source sans serif typeface created for legibility in UI design. With a variety of weights that read easily at all sizes, Source Sans Pro provides clear headers as well as highly readable body text. Inspired by twentieth-century American gothic typeface design, its slender but open letters offer a clean and friendly simplicity.
Merriweather is an open-source serif typeface designed for on-screen reading. This font is ideal for text-dense design: the letterforms have a tall x-height but remain relatively small, making for excellent readability across screen sizes while not occupying extra horizontal space. The combination of slim and thick weights gives the font family stylistic range, while conveying a desirable mix of classic, yet modern simplicity. Merriweather communicates warmth and credibility at both large and smaller font sizes.
From a technical standpoint, we needed to ensure the fonts we provide would perform quickly for users. While our visual designers wanted an array of weights, our developers reminded everyone that this would create a burden on users that have to load extra font families. To compromise, we created different font pairings: a robust option with more font weights and a trimmed down version for quicker load times. Armed with this knowledge, government designers can weigh the options themselves to find which would suit their design and performance needs.
The repeated use of colors found in the interface inventory of government websites informed our color palette. A simple, minimalist palette of cool blue and gray provides a neutral backdrop for brighter shades of red and blue to contrast against. This creates a clean and engaging palette, leaving people feeling like they’re in good hands. The colors are divided by primary, secondary, background, and tertiary colors.
Primary colors are blue, gray, and white. Blue weaves through buttons, links, and headings to bring a sense of calmness, trust, and sincerity through the interface. Clean white content areas allow the typography to “pop” on the page.
Secondary colors are the accent colors of bright blue and red are used sparingly on specialized elements to add lightness and a modern flair. They may be used to highlight important features on a page, like a call to action, or for visual design elements like illustrations.
Background colors are shades of gray used for background blocks of large content areas.
Tertiary colors are used for content-specific needs and are used sparingly, such as in alerts or illustrations.
The range of colors in the palette can be flexibly applied to support a range of distinct visual styles. For example, by abstracting color names, such as primary and secondary, we can support agencies that need to conform the palette to their unique brand’s needs. A change once in the color value spreads throughout the system across buttons, accents, and headings.
Because government sites must be accessible to anyone with a disability, in conformance with Section 50851, the Standards ensures there is enough contrast between text and its background color. Following WCAG 2.0 guidelines52, the Standards provide combinations where the contrast between the text and background is greater than or equal to 4.5:1.
By using bright saturated tints of blue and red, grounded in sophisticated deeper shades of blues and grays, we could communicate warmth and trustworthiness, support a range of distinct visual styles, and meet the highest accessibility standards and color contrast requirements.
The last piece in the building blocks of the design system is how these elements flow in space and provides structure. We provide balanced spacing throughout the type system by placing adequate margins above and below heading elements and paragraph text. By using em’s or relative units, white space is proportionate to the font size and automatically distributes the correct ratio throughout the system. If an agency needs to change a font size, spacing will automatically adjust.
To hold the structure of the content, we provide a 12-column grid system using Neat57, a flexible and lightweight Sass grid by thoughtbot58. We provide an easy-to-use grid system comprised of a grid container to contain the content centered in the page and sections of halves, thirds, quarters, sixths, and twelfths to lay out content. Simple classes, like usa-grid and usa-width-one-half allow developers to quickly mock up page layouts. We provide three breakpoints, which allows the grid to reflow at smaller sizes, and people may always fine tune the breakpoints to suit their content. A flexible grid system allows visitors to quickly read the page.
Typography, colors, and space form the foundation of the design system, which is used to build components like buttons, forms, and navigation.
The U.S. Web Design Standards61 launched in September 2015 as a visual style guide and UI component library with the goal of bringing a common design language for government websites all under one hood. In the two years since we were tasked to unify the design and look of all U.S. government websites, over 100 government projects62 have adopted the standards, helping it evolve, reshape, and move forward in ways we couldn’t imagine. From the Department of Veterans’ Affairs63 to the U.S. Department of Agriculture6417, government teams are coming together to set a new bar for federal government websites. In this short time, we’ve begun seeing consistency and better user experiences across government websites. While the goal was to unify a government design language, the unique expression of it has been multifaceted and boundless. And just like building a house out of Lego blocks, expression within the meaningful constraints of a modular design system creates diverse products that are consistent, not uniform.
By providing designers and developers with easy-to-use tools to deliver the highest quality government websites to the American people, the design system is helping create connections across disciplines and move government designers and developers forward — user research, human-centered design, visual design, front-end, and accessibility best practices all come together.
Lessons Learned: Drafting Your Own Standards Within Your Company Link
Whether you’re a small company or one of the largest governments in the world, you can create your own standards to solve your unique needs. Every pattern library should be different because it should serve the specific needs of the group creating them.
Talk to the people: You’ll need to find out where the problems are and whether or not these problems can be solved by design patterns. Find out if there are common needs across groups. What aspects of what you’re building are required for you to do your job?
Look for duplication of efforts: Where are you repeating yourselves? Where are you wasting time? What takes the longest or is the most challenging when building out websites? Where does friction arise?
Know your values: What your design system will end up looking like will also depend on what’s important to you. What are your values? What principles can guide how you build things?
Empower your team: You need a dedicated group charged with working on this and support from leaders to give you the air cover to do this work. It should be as important as any other project. You’ll need a multidisciplinary team with expertise from user experience research and design, visual design, and front-end development. You’ll need someone to fulfill the role of project manager and product owner to guide the project forward toward the right goals.
Start small and iterate: Figure out what your core needs are, build those out, test them, and listen to what people are asking for. That’s how you’ll find out what is missing. Starting with a limited set of components will save time and give you real answers right away when you start putting it out in the world and in people’s hands.
Don’t work in a vacuum: You’ll need to build consensus, understand what people need, and learn how they build websites, so find people that will use the system. Let that guide your decisions. While you may be more isolated getting the initial system setup, get it out there so you can begin testing and learning. As you build out products with your system and test them with real users, you’ll have the information you need to keep making improvements.
Reuse and specialize: It’s great to see how others have solved problems, and reuse when you can, but know that their solutions are solving their problems. Your problems may need a unique approach. Don’t fall into the trap of “this is what a pattern library should look like” just because someone else is doing it that way.
Promote your system: Get people excited about what you’re doing by talking about the value they’ll get for free by using it: consistent, beautiful, user friendly design with accessible interfaces that will save them time and money.
Be flexible: People don’t like things that are forced on them. Give them opportunities to learn about it and ask questions. Give them permission to make it their own.
When building out a large-scale design system, it can be hard to know where to start. By focusing on the basics, from core styles to coding conventions to design principles, you can create a strong foundation that spreads to different parts of your team. These building blocks can be stacked in many ways to support a multitude of needs and use cases. By building flexibility into the system, users can easily adapt the patterns and designs to support the diverse scope and needs of digital services. Signaling that things can be customized invites people to participate in the system and make it their own. Only when people have a stake in the system will they feel invested to use it and contribute back, making it more robust, versatile, and able to stand the test of time.
It takes a lot of blocks and a lot of time to build these kinds of large design systems, and it’s important to keep people like Joanne in mind. The people on the other side who are scrolling through your designs, clicking your buttons, and filling out your forms so they can access the critical services they need. A solid, usable design system can make all the difference to people like Joanne.
Click to see how the layout behaves on bigger screens.#### Charles WongBeethoven’s “Ode to Joy” as a responsive sheet music page. It consists of two CSS grid layouts – one for positioning the bars within the rows of sheet music, and one for positioning musical notes within the bars. Charles shares more insights into the project [here](https://sejikco.github.io/CssGridSheetMusic/).
#### Dannie VintherA Marvel poster made with CSS and Clip-path. A sprinkle of JavaScript helps avoid layout reflow when images are fully loaded.
#### Erik Davidsson
A great one for football fans! A layout featuring the upcoming football game between FC Barcelona and Real Madrid. Erik brought it to life with many different techniques with fallbacks to make the website usable in older browsers such as IE8 and IE9.
#### MathieuInspired by [Justin Avery’s CodePen](https://codepen.io/justincavery/pen/yaRLYE/), Mathieu submitted a dynamic periodic table built with CSS Grid.
#### Amy DeVoogdInspired by the works of Jen Simmons and Rachel Andrew, [Spacebar](https://amydevoogd.github.io/product-showcase/) is a product showcase for a completely invented product that Amy branded and designed, i.e. all imagery is copyright-free.
#### Ieva OzolīteA mobile-first semantic web page for a [band poster](https://www.swissted.com/products/the-cure-at-canterbury-odeon-1979). Quite impressive for a first experiment with CSS Grid, don’t you agree?
Ethan Horger
The goal here was to try out a blog entry layout that Ethan has always wanted to try, in which the author’s bio would always be displayed on the top-left of the article, legal notices at the bottom, and some kind of quote or supplemental material pinned in the middle of the article. With CSS Grid, the layout degrades to using floats in IE 8 and 9, and doesn’t maintain the article quote in the middle of the article, but is otherwise fully readable.
Tanya Syrygina
Tanya Syrygina used Grid to built a fresh, card-style blog layout.
Nelson Leite
For an e-commerce project, Nelson Leite needed to showcase a product listing with some other content in the middle of the products, displayed differently. His solution: CSS Grid.
Robert Mion
Robert Mion combined CSS Grid and Flexbox to build a responsive supermarket add.
Arturo Ríos
A CSS Grid that can be used comfortably in full-screen mode comes from Arturo Ríos.
Bob Mitro
A simple, responsive blog theme based on CSS Grid layout.
Kev Bonett
Kev Bonnet created a mobile-first e-commerce template with fallback to Flexbox, then fallback to basic 2-column inline-block.
Sven Rothe
Sven Rothe’s grid has equal heights over several rows. So if you add more content in a tile in the first row, the second row will increase, too.
Ismail Ghallou
With his To-Do app layout, Ismail Ghallou proves that CSS Grid can handle even the weirdest layouts. And it’s responsive, too.
Juan Garcia
A page of a video game platform comes from Juan Garcia.
Mark McMurray
A multi-column layout as a CV requires it is a perfect CSS Grid project as Mark McMurray proves.
Marissa Douglass
Ever thought of building an interactive cookbook with CSS Grid? Marissa Douglass did.
Melissa Bogemanns
A photo showcase made with CSS Grid. Available as .zip (6MB)
Tyler Argo
Tyler Argo re-built the Google Play Store layout from scratch using CSS Grid with fallbacks. It works all the way back to IE9 and is even more responsive than the original site.
Mauricio Mantilla
This layout is based on a website that was designed by the company where Mauricio works at. He took part of the layout, which is based on Packery (Masonry) and port it to grid with just a few lines of CSS Grid.
Katherine Kato
A portfolio website layout made with CSS Grid and Flexbox as a fallback.
Donny Truong
A minimalistic blog layout comes from Donny Truong.
Anenth Vishnu
A responsive app layout based on Grid.
Amy Carney
A basic layout (with IE fallbacks and web accessibility in mind) that may be useful for getting projects started or migrated.
Last but not least, before you dive right into the challenge, here are some helpful resources to kick-start your CSS Grid adventure.
Resources and References
Ever thought of building an interactive cookbook with CSS Grid? Marissa Douglass did.
Melissa Bogemanns
A photo showcase made with CSS Grid. Available as .zip (6MB)
Tyler Argo
Tyler Argo re-built the Google Play Store layout from scratch using CSS Grid with fallbacks. It works all the way back to IE9 and is even more responsive than the original site.
Mauricio Mantilla
This layout is based on a website that was designed by the company where Mauricio works at. He took part of the layout, which is based on Packery (Masonry) and port it to grid with just a few lines of CSS Grid.
Katherine Kato
A portfolio website layout made with CSS Grid and Flexbox as a fallback.
Donny Truong
A minimalistic blog layout comes from Donny Truong.
Anenth Vishnu
A responsive app layout based on Grid.
Amy Carney
A basic layout (with IE fallbacks and web accessibility in mind) that may be useful for getting projects started or migrated.
Finally, to get your ideas flowing, some inspiring CodePen experiments that illustrate the magic of CSS Grid:
Are You Ready For The Next Challenge?
That’s right! There will be more challenges coming up very soon, and even more prizes to win! Keep an eye on the magazine or follow us on Twitter so you don’t miss out next time.
If you’re into wristwatches, like me, and are also a fan of the Sketch app (or just want to get better at it), then this is the tutorial for you. In it, you will learn how to create a very realistic and detailed vector illustration of a watch using basic shapes, layer styles and cool Sketch functions such as “Rotate Copies” and “Make Grid.” You’ll learn how to apply multiple shadows and how to use gradients, and you will see how objects can be rotated and duplicated in special ways. No bitmap images will be used, which means you will be able to easily adapt the final image to different sizes and resolutions.
While Sketch is undoubtedly an excellent UI design tool, it can be used as a powerful illustration tool as well. So, in this tutorial, we’ll be walking through the process of creating the iconic Heuer Autavia wrist chronograph, all in vectors.
Taking A Closer Look At Sketch
What really sets Sketch apart from the rest? If you’re a UI designer, you’ll want to take a closer look at its well-rounded set of features that cater to your requirements. Read a related article →
Heuer Autavia
The name “Autavia” comes from a combination of the watch’s target markets (automotive and aviation), and it was unveiled way back in 1962.
The watch became iconic almost instantly, thanks to its simple yet elegant design, optimum readability, built-in chronograph mechanism and rotating bezel. And there’s a beautiful “panda style”: a light-colored dial with three black subdials. You can search on Google Image before we begin to check some reference images of this watch.
The File For This Tutorial
To be able to better follow the steps in this tutorial, download the Autavia.sketch editable file (1 MB). This file will help you follow the process more easily; however, I encourage you to replicate the steps in a new file, with a blank canvas. Have fun!
Set The Artboard
The first step is to create a new document, named Autavia. Set up a new artboard with the same name, 1400 pixels wide and 1600 pixels high, and positioned 0 (X) and 0 (Y).
To make alignment of all elements easier, let’s add some guidelines. First, show the rulers with Ctrl + R. Then, add a vertical guideline at the center of the artboard with a click on the upper ruler, and do the same for the horizontal guide on the left ruler. Pressing Shift when hovering over the ruler will snap to 10-pixel increments, making it easier to set the guides precisely. For correct placement, you could also look at the positions of the guides when you hover over the rulers: 700 pixels for the vertical guide and 800 pixels for the horizontal guide.
The Case
The base of the watch case is a simple circle. Switch to the Oval tool by pressing O on the keyboard, and draw a circle from the intersection of the guides (i.e. the center of the artboard), with a diameter of 950px.
Let’s create the lugs. Select the Vector tool by pressing V on the keyboard, and draw a shape like in the image below.
Press Enter to go into vector point mode, select top-left point, and from the Inspector panel, set Corners to 6. Leave this mode again by pressing Enter. Hold the Alt key, and point to the vertical guide with the mouse to measure the distance from the center.
Duplicate (Cmd + D) this shape, Flip Horizontally, and position it like in the image below. Hold the Alt key and point to the vertical guide with the mouse; leave it there; and use the arrow keys to set the shape to exactly the same distance from the center, like the first one. Now, measure the distance from the horizontal guide. We’ll need to position the bottom lugs the same distance from the center.
Duplicate both of these shapes, Flip Vertically, and move them to the bottom part of the circle, as shown in the image below. Hold Alt, point to the horizontal guide with the mouse, and use the arrow keys to move the shapes until they are the same distance from the center, like the top lugs.
Now, select all shapes, including the circle, and simply merge them into one shape using the Union operation from the top toolbar; give the resulting shape the name case. Duplicate this shape, because we’re going to use it later to add texture, and hide it by clicking on the eye icon next to the layer’s name, in the Layers panel.
It’s time to add some style to our case, to give it a metallic appearance. Turn off the Borders, then click on Fill, choose Linear Gradient, and add a gradient. Color steps can be added by clicking on the gradient line directly on the shape. The gradient properties are, from top to bottom:
#4B4B4B
#5F5F5F
#BEBEBE
#FFFFFF
#B5B5B5
#787878
#E4E4E4
#F0F0F0
#787878
#B5B5B5
#FFFFFF
#C4C4C4
#4B4B4B
#4B4B4B
Using the Vector tool (V), draw four triangles over each lug, from the inner side, to add some depth to the case. For the color, use #545454, and turn off the borders. Use the image below as a reference.
Let’s fit the triangles inside the case. Select the case shape and all four rectangles, and click on Mask in the top toolbar. The result of this masking operation will automatically be placed in a new group in the Layers list. Change the name of this group to case.
Remember the copy of our case shape? Unhide it, and move it into the case group, as the top layer. Click on Fill and choose Noise Fill, set Intensity to 15%, and apply Motion Blur with an Amount of 20px, to create a brushed metal texture.
The Bezel
Let’s move on and create the bezel. The bezel is made of two rings: an outer with toothed edging, and a black inner with digits and minute marks.
Toothed Edged Outer Ring
First, we will create an outer ring with toothed edging. Draw a circle from the center of the artboard, with a diameter of 950px. Turn off the borders.
To create the toothed edging, first create a small triangle, 10px by 8px, using Triangle from Insert → Shape in the menu bar. Click on the Center Align icon in the Inspector panel to align the triangle to the center of the artboard and the circle (since everything is in the center of the artboard), and make sure that its downside overlaps with the circle, and turn of the borders.
We need those triangles around the circle, and it would be a very time-consuming (and complex!) task to manually create and rotate each and every triangle. Luckily for us, Rotate Copies — a magnificent feature that can do both at the same time — has come to the rescue. So, select the triangle, and choose Rotate Copies from Layer → Paths in the menu bar. The following dialog will let you define how many additional copies of the selected element to make. Enter 259; so, in total, we will have 260 triangles around the circle that will be the toothed edge. After you have entered this value and confirmed the dialog, you will be presented with all of the triangles and a circular indicator in the middle.
Note: Performing this step is very CPU- and memory-intensive. If you are working on a modern machine, you probably will not experience any issues; but if your Mac is a bit older, then your mileage may vary. In general, when working with a large number of copies, try to turn off borders to avoid getting stuck and to achieve the result of the operation faster.
Move the indicator down until it is at the intersection of the guides — and voilà! we have 260 triangles around the circle. Please note that if you miss putting the circular indicator (the center of rotation) right at the intersection of the guides, the triangles won’t be placed perfectly around the circle, and you won’t be able to alter their position anymore as soon as you click anywhere else on the canvas; but it will still be possible to change the individual elements after accessing the related Boolean group.
Performance tip: Alternatively, you can perform this step in two passes, which will be a much less CPU-intensive task for your Mac. First, enter 129 for the additional copies; so, in total, you will have 130 triangles around the circle. Move the indicator down until it is at the intersection of the guides. Second, duplicate (Cmd + D) this shape, and then, using Rotate from the top toolbar, rotate it by 4 degrees. Finally, select both shapes and merge them into one using Union from the top toolbar.
Now, go inside this Boolean group and erase all triangles except the central one at the top and the six to the left and right of its sides.
Use Rotate Copies again, but this time we need 11 additional copies. Align the circular indicator to the intersection of the guides.
Select the resulting Boolean group and circle, and perform a Union operation. Name the resulting object outer bezel, and apply the following styles.
First, a Gradient fill:
#C6C6C6
#CFCFCF
#EFEFF2
#E7E7E7
#FFFFFF
#E7E7E7
#D2D3D5
#CBCBCB
#CBCBCB
#BCBCBC
We will now use Inner Shadows and Shadows to make it look slightly raised.
Let’s add a light Inner Shadows effect with the following properties:
Color: #FFFFFF
Alpha: 50%
X: 1; Y: 1; Blur: 2; Spread: 1
Then, add a dark Inner Shadows effect:
Color: #000000
Alpha: 10%
X: -1; Y: -1; Blur: 2; Spread: 1
Finally apply Shadows effect:
Color: #000000
Alpha: 50%
X: 0; Y: 0; Blur: 4; Spread: 1
This is what the outer bezel looks now with all of the styles applied.
To make this a ring, we need to make the hole in the middle. Draw a circle in the middle of the artboard, with a diameter of 780px, select “outer bezel” and this circle, and simply apply a Subtract operation from the top toolbar. Name the resulting shape outer ring.
Black Inner Ring
For the inner ring, first draw a circle from the center, with a diameter of 930px. Add an Outside border with a Thickness of 1 and the Color set to #313131.
Next, change Fill to Angular Gradient, and adjust the gradient with the following parameters:
#303030
#343434
#282828
#484848
#292929
#333333
#282828
#343434
To add subtle 3D look, add Inner Shadows, with the Color set to #FFFFFF at 30% alpha. Set Blur to 4, Spread to 2 and both the X and Y positions to 0. Then apply Shadows, to make it look raised a bit. Set Color to #000000 at 50% alpha, Blur and Spread to 1 and the X and Y positions set to 0. Name this shape inner bezel.
We have two scales on our inner ring: the minute and hour scales. Let’s deal with the minute scale first. The minute scale consists of minute marks and numerals at five-minute intervals. We will use circles for the minute marks. So, draw a circle with a diameter of 16px, set Color to #FDFDFE, and turn off the borders. Move it away 48px from the outer edge of the inner ring. Hold the Alt key, point to the circle with the mouse, leave it there, and use the arrow keys to reposition the shape until the spacing is correct. Center it to the artboard and inner ring using Center Align from the Inspector panel.
We actually need 59 more of these marks, so go to Layer → Paths, select Rotate Copies, enter 59 in the dialog box, click OK, and align the circular indicator to the intersection of the guides. Rename this resulting shape to minute marks, and delete every fifth mark, starting from the top-middle one.
Let’s add minute numerals at those five-minute positions. Unfortunately, we can’t use “Rotate Copies” to distribute text layers, so we will need to position them manually. For the numerals, we will use the slightly rounded Rubik font family from Google. Add the “60” at the top, with a font size of 49, a Medium weight, Center alignment, and a #FDFDFE fill. Move it 20px away from the outer edge of the inner ring, and center it to the inner ring horizontally. Now, duplicate this number by pressing Cmd + D on the keyboard, and select Rotate from the top toolbar. Because we need to rotate the numbers around the center of our artboard, click and drag the crosshair marker to the intersection of the guides, and rotate it 30 degrees. Continue duplicating and rotating each number one after another, without letting the selection go. That way, the anchor will stay in the middle and you won’t have to move it every time.
Finally, edit the numbers one by one, and change the digits to the corresponding minute marks.
Next, add the hour scale above the minute scale. First, duplicate the number “60,” change the font size to 26 and the number to 12, and move it up using the arrow keys so that it’s 1px away from the outer edge of the inner ring. Now, using the method explained above, add the remaining hour numerals.
Let’s finish the inner ring markings by adding a triangle at the 12-hour position. First, delete the “60” and “12” numbers from both scales, select the Triangle tool from Insert → Shape, and draw a 60px by 65px triangle. Move it 5px away from the outer edge of the inner ring, and center it to the inner ring horizontally. For the color, use the same color that we used for the marks and digits — #FDFDFE — and turn off the borders.
We also need to create the hole in the middle, just like we did for the outer ring, to make it look like the real ring. So, draw a circle in the middle of the artboard with the diameter of 780px, select inner bezel and this circle, and perform a Subtract operation from the top toolbar. Name the resulting shape inner ring.
Select all of the bezel layers, and place them inside the group rotating bezel.
The Dial
It’s time to work on the dial. First, we will create the outer ring of the dial.
Switch to the Oval tool (O), and create a circle in the middle of the artboard, with a diameter of 768px. Set Fill to #CCCCCC, and add an Outside border, with a Thickness of 4px and the Color set to #D9D9D9. Also, apply Inner Shadows, with the Color set to #000000 at 50% alpha and the Blur set to 10. Name this shape outer dial.
For the base of the dial, create a circle in the middle of the artboard, with a diameter of 706px, a Fill set to #FAFBF9 and an Outside border with a Thickness of 4px and a Color of #D9D9D9. Add Inner Shadows, with the Color set to #000000 with 50% alpha and a 6Blur. Give this shape a name of inner dial.
One-Fifth of a Second Scale
To break the ground, we’ll add the scale for the one-fifth of a second, which means that 300 thin markings should line the outer rim of the dial. Create a rectangle with a width of 2px and a height of 18px, with the Fill set to #050B05 and the borders turned off. (Turning off borders will help avoid the Mac’s spinning beach ball of death.)
Center the rectangle to the inner dial horizontally, and move it 19px away from the the outer edge of inner dial. Once again, we’ll use “Rotate Copies” to create the scale. Go to Layer → Paths, select Rotate Copies, enter 299 in the dialog, click OK, and align the circular indicator to the intersection of the guides. Rename this resulting shape to one-fifth of a second scale.
60-Second Markings
Next, we’ll add 60-second markings. These markings are the same as the “one-fifth of a second” markings but longer. So, create exactly the same rectangle as in the previous step, but make it 36px high, and position it the same way (19px from the outer edge and aligned horizontally to inner dial). Open the Rotate Copies, enter 59 in the dialog box, click OK, and align the circular indicator to the intersection of the guides.
We don’t need the marks at the hours’ positions, so we will have to delete every fifth mark, starting from the one in the 12:00 position. You can also use a rotating bezel as a guide for which mark to delete.
Rename this resulting shape to 60 seconds markings.
Five-Minute Markings
Let’s add the minute markings. Create a rectangle with a width of 4px and a height of 18px, with the Fill set to #050B05 and borders turned off. Center it to the inner dial horizontally, and move it 19px away from the outer edge of inner dial. Because we need 12 hour marks in total, go to Rotate Copies, enter 11 in the dialog box, click OK, and align the circular indicator to the intersection of the guides. Give this shape a name of 5-minute markings.
15-Minute Markings
For the 15-minute markings, we will use circles. Draw a circle with a diameter of 16px, align it horizontally with the inner dial, and move it 20px away from the top. Set its Fill to #E5BF8E, and add Shadows with the Color set to #000000 at 50% alpha, the Blur set to 2 and the Spread set to 1.
Use Rotate Copies to add circles at the quarter-hour positions. At the end, delete the one at the 12:00 position. Rename this shape to 15 minute markings.
Hour Markings
In this step, we will create hour markings. Select the Rectangle tool (R), and create a rectangle with a width of 30px and a height of 70px, with the Fill set to #E7E7E7. Add an Outside border, with a Thickness of 3px and the Color set to #FFFFFF. Position it at 12:00, and move it 43px away from the the outer edge of inner dial. Apply Inner Shadows, with the Color set to #000000 with 30% alpha and a Blur of 5, and Shadows with the exact same color, a Blur of 2 and a Spread of 1, to make it look slightly raised from the inner dial. This will be the base of the hour mark.
We need to add two smaller rectangles at the top and bottom to finish the hour mark. The easiest way to do this is to duplicate (Cmd + D) the rectangle, turn off borders and shadows, change the height to 16px and the Fill to #E5BF8E, and alter the Inner Shadows so that the Color is now set to #FFFFFF with 60% alpha, the Y to 1 and the Blur is 2.
To add a rectangle to the bottom, duplicate the rectangle that we just created, Flip Vertically, change the Fill to #393F3B, and align it to the bottom of our base, by selecting both of these rectangles, right-clicking, and selecting Align Bottom. Finally, select all three rectangles and group them into an hour mark group using Cmd + G.
Duplicate the hour mark group and turn the duplicate into a symbol by right-clicking and selecting Create Symbol. Name this symbol hour-mark. Now we need to distribute hour-mark symbols around the dial. Select the hour-mark symbol, choose Rotate from the top toolbar, drag the crosshair marker to the center of the artboard (the intersection of the guides), and rotate it to the position of 1:00 (30 degrees). Continue duplicating and rotating the symbol (in 30-degree increments), without letting the selection go. Finally, delete the hour-mark symbols at the positions of 3:00, 6:00 and 9:00, since we won’t need them.
The hour mark at 12:00 needs to be a bit different, and it should consist of two narrower hour marks. This is why we kept the original hour mark group! We will simply modify the existing hour mark, by selecting all three shapes inside the hour mark group and changing the width to 20px from the Inspector Panel on the right. Next, duplicate this group, move it 9px to the right using the arrow keys; then, select both groups, and place them in a new group named 12-hour mark; align them horizontally with the inner dial. Lastly, select all hour marks and put them in the hours group.
Please note that the same can be done with symbols as well, because they can be resized while keeping the internal spacing intact.
Subdials
Subdials give information not provided by the main watch dial, and they are common features of chronograph watches. Chronographs use subdials to keep track of seconds and of elapsed minutes and hours. In this step, we will create three subdials: an active seconds subdial, a 30-minute counter and a 12-hour counter.
Active Second Subdial
This subdial shows continuously running seconds. Let’s start by drawing a circle from the center of the artboard (i.e. the intersection of the guides), with a diameter of 204px. Add a Center border with a Thickness of 1 and the Color set to #424242. Apply Inner Shadows with the Color set to #00000 at 60% alpha and the Blur set to 20, to make it look like it’s inserted in the main dial. Name this shape subdial base.
Let’s add the markings. First, we will add tiny markings. Create a white rectangle with a width of 4px and a height of 25px and the borders turned off. Align it horizontally to the subdial base, and position it 5px away from the top of the subdial base. Use Rotate Copies to create a 12-marking scale. This subdial has tiny markings at five-second interval, so we need to delete every second marking, starting from the top one (at the 60-second position).
Next, we need to add a bit bolder markings at the 60, 20 and 40 positions. Create a rectangle like the previous one, but make it a bit wider: 6px in width and 25px in height, and at the same position. Open Rotate Copies and create a three-marking scale.
Let’s add the remaining three markings at the 10, 30 and 50 positions. Create a rectangle like the previous one, but make it a bit longer, 6px in width and 35px in height, and position it 5px away from the bottom of the subdial base. Open Rotate Copies and create a three-marking scale.
Now, we need to add numbers to the subdial. Add the 60 at the top; for the font, use Rubik again, with a font size of 26, a Regular weight, Center alignment and a #FFFFFF fill. Move it 30px away from the outer edge of subdial base, and center it horizontally.
Duplicate this number. Select Rotate from the top toolbar, drag the crosshair marker to the intersection of the guides, rotate it 120 degrees, and change the number to 20. But we don’t want the number to be rotated this way; it should stay normal, like the 60. To do that, set the Transform to 0 in the Inspector Panel on the right. Now, use the arrow key to move the number away from the marking, to the left by 6px (press the left arrow key six times).
Using the same method, add the number 40 at the 40-seconds position, but this time move the number 6px to the right.
Let’s add a hand to finish the subdial. First, draw a white circle from the middle of the artboard, with a dimeter of 30px and borders turned off. Then, draw a white 6px by 68px rectangle, and Align Horizontally with the circle we’ve just created, making sure they overlap. Select both shapes and perform a Union operation to create one object. Add Shadows with the Color set to #000000 with 70% alpha, the Blur set to 8 and a Spread of 2, to raise the hand from the subdial.
Create a small circle on top, with a diameter of `12px`. Set the **Fill** to `#A3A3A3`, turn off borders, and add black **Shadows** with `50%` alpha and a **Blur** of `2`. Now duplicate this circle, turn off **Shadows**, change the **Fill** to `#353535`, and scale it down to `50%`. These two circles will represent a small screw to keep the hand in place. Group all shapes for the hand, and name this group `hand`.
Select all of the shapes that we used to create the subdial, and group them into the `subdial` group. We will use this group as a base to create all three subdials.Now, duplicate the group and rename it to `active second subdial`, and then move it to the 9:00 position. Using the left arrow key, move the subdial to the left until it’s `55px` away from the left edge of `inner dial`. Add the vertical guide at the center (`504px`) of `active second subdial`. This will help us to adjust the position of the hand. Use a simple math operation to determine where the vertical center is: The subdial’s diameter is `204`, so the radius is `102px` — just add `102px` to the current X position in the Inspector Panel on the right.
**30-Minute Counter**This subdial is used to record events longer than one minute (and lasting up to 30 minutes) and it’s placed on the right side of the dial, at the 3:00 position.We’ll be using the `subdial` group as the base for this one, so duplicate the `subdial` group, rename it to “30 minute counter” and alter it a bit. Delete the tiny markings scale. Create a new one, in the same way we did above, but with `30` markings. Go into this new scale and delete markings that are in the same positions with the bold markings, and then change the numbers to `30`, `10` and `20`. Move this subdial to the right until it’s `55px` away from the right edge of `inner dial`, and then add the vertical guide at the center (`896px`). Use the image below as a reference.
**12-Hour Counter**This subdial is used to record events longer than 30 minutes (up to 12 hours), and it’s placed at the bottom side of the dial, at the 6:00 position.We will use the remaining `subdial` group to create the 12-hour counter. This counter has smaller markings at half-hour intervals, which means that we have 24 of these around the circle, and longer markings at the hour positions, which means that there are 12 of these around the circle.Once again, **Rotate Copies** will help us creating those markings. For the half-hour markings, use a rectangle with the size of `4px` by `15px`, and for the hour marks, use a longer and bit bolder rectangle, `6px` by `25px`. Remember to delete those half-hour markings that are in the same position as the hour markings, since there’s no need to have both. Also, add a small “Swiss T” title `12px` away from the hand, using a white **Rubik Medium** font with the size set to `10px`, **Center** alignment and a spacing **Line** of `10`.When done, change the group name to `12 hour counter`, move it into position at 6:00, `55px` away from the bottom edge of `inner dial`, and add a horizontal guide at the center (`996px`). Use the image below as a reference.
Our subdials are in place, and we can set the time for each subdial if we want. Simply select the `hand` group, select **Rotate** from the top toolbar, drag the crosshair to the intersection of the guides, and perform the rotation.
#### Finishing the DialIt’s time to put some branding on the dial. Jump over to Wikimedia Commons and download the [Tag Heuer](https://commons.wikimedia.org/wiki/File:TAG_HEUER_logo.svg) logo in SVG format. This is the current version of the logo; we will need to modify it a bit because we are recreating a watch from pre-Tag era.Open the logo in Sketch, and delete everything except the red rectangle that holds the word Heuer. First, change the color of the letters to `#2E2E2E`. Then, select the rectangle, turn off **Fill** and add **Borders**, set the **Position** to **Outside** and the **Color** to `#2E2E2E`. Change the name of the group to `Heuer logo`.
Bring the modified logo into our design. Switch to the **Scale** tool in the top toolbar, and in the dialog box enter `50px` in the height field, to adjust the size of the logo. The scale function will automatically calculate the correct width for any given height. Align the logo horizontally with `inner dial`, and place it `200px` away from the top. Once again, select the rectangle that holds the lettering, and set the border **Thickness** to `3`.
Let’s add the watch’s name above the logo. Download the Walkway Expand font family (it’s free), install only the “Walkway Extend Black” font, and type the word “Autavia.” Place it 3px above the logo, and align it horizontally with the logo. Set the Size to 24px, the spacing Character to 2, the Line to 24 and the Color to #2E2E2E (the same one that we used for the logo).
Next, convert the text block into vector shapes, by right-clicking and selecting Convert to Outlines. Then, modify the “A” a bit to make it look similar to the original “A” in vintage Heuer watches. Select the resulting vector shape, press Enter to go into vector point mode, zoom in close enough (I zoomed in to 800%) and select top-left point of the letter “A,” and move it 1px to the left. Then, select the top-right point, and move it to the right by 1px. After that, select the small triangle inside the letter, add one point anywhere on the side line, and drag it up until it’s on the same level as the middle point (a horizontal red line will appear). Adjust the position by dragging them a bit left and right inwards, and then readjust the positions of the other points to make an even width on the slanting lines. Use the image below as a reference.
Do the same thing for the remaining “A” letters.
To finish the dial, we need to do one more thing: add the one-fifth of a second scale to outer dial. This is where the proper naming of shapes and groups helps. In the Layers panel on the left, find one-fifth of a second scale and copy it. Find the outer dial shape and paste it over. We need to scale it up, because it’s smaller than outer dial. Select the Scale feature, and scale it up to 114%. Now it should be inside outer dial, right where we want it to be!
Apply a Gaussian Blur with an Amount of 2px, and lower the Opacity to 40%. Just to be sure that the scale doesn’t exceed the outer dial, select both shapes and perform a Mask operation from the top toolbar. Sketch will place the result automatically into a group. Give this resulting group the name outer dial finished.
Performance tip: Gaussian blur is a CPU- and memory-intensive process. I noticed that Sketch’s performance is better if Show Pixels is turned on. This setting can be enabled by going to View → Canvas → Show Pixels or by using the Ctrl + P shortcut. Given how complex the illustration is, periodically saving and restarting Sketch seems to help a bit, too.
The Watch Hands
It’s time to create the watch hands. We need to create the hour hand, the minute hand and the large second hand (which is part of the stopwatch).
The Hour Hand
Let’s start with the hour hand because it’s physically closest to the dial. Similar to how the subdial hands were made, this hand will also be made of a circle and rectangle. Zoom in a bit and create a circle in the middle of inner dial, with a diameter of 58px. Then, add a rectangle, also in the middle of inner dial, with dimensions of 30px by 246px. The rectangle should be 150px away from the top of inner dial.
Make sure the rectangle is still selected. Enter shape editing mode by double-clicking the rectangle or hitting Enter. Now, hold Command and click on the top segment to add a point in the exact middle. Push this point up by 20px.
Select both shapes (the rectangle and circle), and merge them with Union from the top toolbar. Add a Fill of #585858, and add Borders with a Color of #E4E4E4, a Position of Outside and a Thickness of 2
Let’s give the hand a three-dimensional appearance. First, add a Linear Gradient fill on top of the existing fill; use #4A4A4A with 100% alpha for the first color stop and white with 0% for the last stop. Bring the gradient to a horizontal position with the left-pointing arrow in the color dialog. Now, add another point with a double-click on the gradient axis in the color dialog, and move it to the exact middle by pressing 5 on the keyboard. Give it 100% alpha, and make sure its color is #4A4A4A. Add another one to the right, and also move it to the center and then 1px to the right using right arrow key. Change the color of this stop to #898989 with 20% alpha.
Next, apply white Inner Shadows with 50% alpha and a Blur of 2. Then apply black Shadows with 70% alpha, a Blur of 6 and a Spread of 1.
To finish the hour hand, we need to fill it with a luminescent color, so that it is possible to check the time in the dark. To do this, draw a rectangle with a size of 16px by 100px, and position it 22px away from the top of the hour hand. For the Fill, use #FFE4C0. Enter shape editing mode, select the two bottom points, and set the Radius to 3px. Add a Center border with a Thickness of 2, and set the Color to #626262. Add Inner Shadows to make it look like it’s inside the hand. For the Color, use #000000 with 30% alpha, a Blur of 3 and a Spread of 3.
Select all of the hour-hand shapes, and place them in the hour hand group using Cmd + G.
The Minute Hand
The minute hand is basically the same as the hour hand but longer and with a smaller circle. Duplicate the hour hand, hide the original layer, and name the new one minute hand. Go into the group, select the circle from the merged shape, go to Layer → Transform → Scale (or Cmd + K), and enter 80% (to scale it down by 20%).
Make sure that the merged shape is selected, enter shape editing mode, select the top three points (hold Shift while clicking on the points to select them all), and push them up by 90px. Holding Shift and the up arrow key will move the selection in 10-pixel increments.
Click on the gradient fill and bring the gradient to the opposite side, using the right-pointing arrow twice in the color dialog. Also, edit the Shadows: Lower the alpha to 50%, set the Blur to 5 and the Spread to 1.
Finally, select the luminescent rectangle on top, enter shape editing mode, select two top points, and push them up by 90px as well. The minute hand is now complete.
Large Second Hand
This hand moves only when the stopwatch mode is on, and it measures the elapsed time in seconds.
This hand is also made of a circle and rectangle, and we will create it using the same method. Hide the minute hand group, and draw a circle with a diameter of 30px in the middle of inner dial. Add on top a tiny rectangle with a size of 8px by 408px, and move the bottom two vector points in by 2px each to form a trapezoid; set it in the middle and 26px away from the top of inner dial. Use the same Fill of #404040 for the circle and rectangle, with borders turned off.
Add a point in the exact middle of the top and bottom segments, and push the top one 6px up and the bottom one 6px down.
Merge both shapes into a large seconds hand group using Union. Apply white Inner Shadows with 40% alpha and a Blur of 2, and black Shadows with 50% alpha, a Blur of 4 and a Spread of 1.
Finally, add a screw on top to hold the watch hands in place. Create a circle with a diameter of 14px in the middle of the artboard, with a Fill of #5F5F5F and borders turned off. Add a black Shadows effect with 50% alpha, the Blur set to 2 and a Spread of 1.
Add one more circle on top, this time with a diameter of 6px and the Fill set to #4C4C4C. Add an Outside border with a Thickness of 1 and a Color of #888888, and apply a black Inner Shadows effect with 50% alpha and a Blur of 3. Select both circles and group them into a screw group.
Now we can set the time to 10:08:24. Select large seconds hand, click on Rotate in the top toolbar, drag the crosshair marker to the intersection of the guides, and rotate it by 144 degrees to set it at 24 seconds.
Bring back minute hand into the scene by unhiding it, and then rotate it by 48 degrees, just like we did with large seconds hand, to read 8 minutes.
Finally, unhide hour hand, and set it to a little after 10:00 (-56 degrees — rotating counter-clockwise is much easier than going around the full circumference).
The Crown
Time to create the watch crown. Start with a rectangle of 78px by 162px. Align it vertically to the artboard, turn off borders, and apply Linear Gradient with the following parameters, from top to bottom:
#989898
#A5A5A5
#E8E8E8
#8C8C8C
#787878
Enter shape editing mode, select the two vector points on the right side of the rectangle, and set the Radius to 10px.
Hold Cmd and click on the right segment to add a point in the middle. Push this point right by 20px and double-click on it to turn it to a Mirrored vector point.
Select the Vector tool (V) and draw a line like in the image below. Set the border Color to #F0F0F0 and the Thickness to 3. Add a black Shadows effect with 20% alpha, and set Y and Blur to 2.
We will use this line to create toothed edges around the crown. Go to Arrange → Make Grid, and in the dialog box set Rows to 13, Margin to 1px and Columns to 1, and click on Make Grid. “Make Grid” will distribute the selected layers with predefined spacing between them.
We have filled the top half of the crown. The easiest way to fill the bottom half is to select all paths created with “Make Grid,” group them, Duplicate (Cmd + D), Flip Vertically, and move it to the bottom half of the crown. Then, select both groups and the crown shape, and perform a Mask operation so that none of the created elements go outside of the crown shape. Name the resulting group crown.
Drag the crown to the left, and send it behind the case in the Layers panel.
The Pushers
Chronograph buttons (or “pushers”) start, stop and reset the chronograph function without interfering with the watch. Let’s add those buttons to the side of the case. Each pusher is made of two rectangles, one on top of the other. Draw a first rectangle of 28px by 74px, turn off borders, and apply a Linear Gradient for the Fill:
#8D8D8D
#D3D3D3
#8A8A8A
Apply black Inner Shadows with 30% alpha, and set Y to -13 and Blur to 10.
Draw second rectangle on top, 20px away from the left segment, with a size of 86px by 106px, and Align Vertically with the one below. Set Radius to 14px, make sure borders are turned off, and use Linear Gradient for the fill:
#8C8C8C
#CACACA
#FFFFFF
#BABABA
#707070
Select both rectangles and group them in the pusher group, and Align Vertically to artboard. Duplicate this group, because we need two pushers, one above and one below crown.
Send them below case in the Layers panel, and rotate the first by -30 degrees and the second by 30 degrees, with the crosshair marker set at the intersection of the guides.
The Strap
We’re almost there. For the final touches, we will create the straps.
Add a rectangle of 476px by 376px between the top lugs, and align it vertically with the artboard. Select the top two vector points, and set the Radius to 60px. Then, move the bottom two vector points by 10px each to form a trapezoid.
Uncheck Borders, click on the Fill button, switch to Linear Gradient, and create a gradient using the following settings:
#636363
#3B3B3B
#2B2B2B
#000000
#1F1F1F
#000000
Add a horizontal Linear Gradient to imitate the leather embossing effect that real stitches make. The settings are:
#0C0C0C with 40% alpha
#0E0E0E with 10% alpha
#2E2E2E with 80% alpha
#181818 with 10% alpha
#181818 with 10% alpha
#2E2E2E with 80% alpha
#0E0E0E with 10% alpha
#0C0C0C with 40% alpha
Add Noise Fill on top, with Soft Light blending and an Opacity of 20%, to add a subtle texture. Finally, add white Inner Shadows with 10% alpha, and set Y to 15 and Blur to 10.
Finish the strap by adding contrast stitching that is common on the straps. Create a Rounded Rectangle (U) of 10px by 30px, with a Radius of 5. Set the border Color to #3B3B3B, with an Inside position and a Thickness of 1. Change the Fill to #E6E6E6.
Add texture by using Noise Fill on top, with Overlay blending and an Opacity of 70%. Then, apply Shadows with the color set to #000000 with 50% alpha and the Blur and Spread set to 2.
Use Make Grid to add stitches vertically. In the dialog box, set Rows to 7, Margin to 2px and Columns to 1.
Select all of the stitches, duplicate and move them to the right side of the strap.
Select all of the shapes that are part of the strap (the stitches and strap shape), and group them into the strap top group. Move this group just above case in the Layers panel. To create a bottom strap, duplicate the strap top group, Flip Vertically, move it between the bottom lugs, and change the name to bottom strap. Select and put all watch elements into one group, autavia. Our watch is now officially finished!
Finally, let’s add a background. Create a rectangle of the same size as the artboard, set the Fill to #0D0F0E, and push it below the autavia group.
Conclusion
Now you know how to recreate one of my favorite watches. Chronographs are magnificent pieces of engineering and state-of-the-art technology, and while the tutorial probably wasn’t easy, the result is quite good in my opinion — not unlike the process of building a real watch!
The next step, of course, is to design your own favorite illustration. Select a watch (or another object you like), and be sure to get as many photos from different angles, so that you can replicate all of the important details. As you can see, there are certain tools and features in Sketch that you can master to create similar objects; use them to speed up and simplify the whole process.
Each wristwatch has different features, but in this case we’ve covered most of them, and I’m pretty sure that you can now reverse-engineer any other watch.
Next, imagine exporting these elements to SVG format and animating them. Imagine recreating not only how they look, but also how they work!
Finally, if you have any questions, please leave a comment or ping me on Twitter. I will gladly help you.
(This is a sponsored post). As web design focuses more and more on good user experience, designers need to create the most usable and attractive websites possible. Carefully applied minimalist principles can help designers make attractive and effective websites with fewer elements, simplifying and improving users’ interactions.
In this article, I will discuss some examples of minimalism in web design, things to consider when designing minimalist interfaces, and explain why sometimes “less is more”. If you’d like to get more creative with your own designs, you can download and test Adobe XD, and get started right away.
Selecting An Effective Color Scheme
When designing a new app, it’s often difficult to decide on a color scheme that works well, as there are an infinite number of possible color combinations out there. Read a related article →
A Short History Of Minimalist Design
Some web designers mistakenly think of minimalism as a primarily aesthetic choice. To avoid the pitfall of focusing only on aesthetics, let’s be clear about the roots of minimalist design.
While it might be a newer trend in web design, the underlying ideas have been around for much longer. When discussing minimalist design, a person might naturally think of traditional Japanese culture. Japanese culture values balance and simplicity. Japanese architecture, interior design, art, and graphic design have long employed minimalist aspects.
As a Western movement, minimalism began early in the 20th century. Influenced by the introduction of modern materials, such as glass and steel, many architects began to employ minimalist designs in their buildings. Ludwig Mies Van der Rohe, the German-American architect, was one of the pioneers of the minimalist movement. He is credited with first applying the phrase “less is more” to architectural design.
The less-is-more attitude quickly moved from architecture to other arts and industries: interior and industrial design, painting, and music. As a direction of visual design, minimalism became popular in the 1960s, when artists moved toward geometric abstraction in painting and sculpture. The artistic movement found its impression in the artwork associated with the Bauhaus school. One well-known minimalist artist who influenced the movement was Donald Judd, whose artwork is full of simple shapes and color combinations.
In diverse spheres of visual arts, a key principle of minimalism was leaving only the essential part of a feature, in order to focus the recipient’s attention as well as to enhance the overall elegance. As Donald Judd said, “A shape, a volume, a color, a surface is something itself. It shouldn’t be concealed as part of a fairly different whole. The shapes and materials shouldn’t be altered by their context.”
What Is Minimalist Web Design?
Today, minimalism has reemerged as a powerful technique in modern web design. It became popular as a reaction to a trend of increasing complexity in web design. (Visual complexity has been shown to affect a user’s perception of a website: The more elements a design has, the more complex it will look to the user.) Applied correctly, minimalism can help us focus our designs in order to simplify user tasks. A study conducted by EyeQuant suggests that clean design results in lower bounce rate. Minimalism has brought additional benefits to websites, in faster-loading times and better compatibility between screen sizes.
Perhaps one of the most well-known examples of minimalism in web design is Google Search. Google has prioritized simplicity in its interfaces ever since its beta offering in the 1990s. The home page is designed entirely around its central search function. Anything unnecessary to the function, other than branding, was avoided.
Its simplicity might lead one to believe that minimalism is uncomplicated, but under the surface lies far more than just “less is more.” Let’s define characteristics of minimalism.
Only the Essentials
A minimalist strategy in web design is one that seeks to simplify interfaces by removing elements and content that do not support user tasks. To create a truly minimalist interface, a designer has to prioritize elements rigorously, showing only those elements of the highest importance and stripping away everything that would distract users from what’s important (such as superfluous decorative elements). Every item in a design, whether an image or copy, should have a purpose; it shouldn’t be included unless it’s necessary to make the message clear. As Joshua Becker mentions in the book The More of Less, “You don’t need more space. You need less stuff.”
At the same time, be sure that you aren’t making your users’ primary tasks more difficult by removing or hiding content that they need. The idea is to make the message more clear, not more hidden. Thus, design around the content, and leave just enough visible elements (such as primary navigation) so that users don’t get confused.
Negative Space
It should be no surprise that the most common element in minimalism is no element at all. Negative (or white) space is the most important feature of minimalism and gives it much of its power. Negative space is just the empty space between visual elements. More empty space means more emphasis on existing elements. In Japanese culture, it’s known as the ma principle: treating the space between objects as a means to emphasize the value of those objects.
While negative space is often called white space, it doesn’t have to be white. Some websites use full-color backgrounds to energize a blank canvas.
Visual Characteristics
In a minimalist design, every detail has significance. What you choose to leave in is vital:
Flat texture
Minimalist interfaces often use flat textures, icons and graphic elements. Flat interfaces don’t make use of any of the obvious highlights, shadows, gradients or other textures that would make UI elements look glossy or 3D.
Vivid photography and illustration
Images are the most prominent form of artwork in minimalist design; they enable an entire world of emotional connection and set an atmosphere. But a photo or illustration has to follow the principles of minimalism. A wrong image (such as a busy photograph full of distracting elements) would negate the benefits of the surrounding minimalist interface and ruin the integrity of the layout.
Limited color scheme
Color has great potential in web design because it’s able to set both informative and emotional connections between the product and the user. Color can add visual interest or direct attention without needing any additional design elements or actual graphics. Designers aiming for minimalism tend to squeeze the maximum from just a few selected colors, and it’s not that rare to use just a single color (a monochrome color scheme).
Dramatic typography
In addition to color, typography is a core visual element. Bold typography brings immediate focus to the words and content, while helping to craft a much larger intriguing visual.
Contrast
Because the goal of minimalist design is ease of use and efficiency, high-contrast copy or graphic elements might be a good choice. High contrast can direct the user’s attention to important elements and make text more readable.
Best Practices
Because a minimalist design demands the same level of clarity and functionality as a “normal” design, but with fewer elements, it can be a challenge for designers to create.
Have a Single Focal Point Per Screen
The minimalist philosophy centers on the idea of designing around the content: Content is king, and the visual layout salutes the king. The aim is to make the message clearer not just by stripping away distractions, but also by keeping focus on what’s important. Because minimalism involves stripping away elements that are unnecessary, a strong focal area is important.
Set Great Expectations With the Top Area of the Screen
What is visible on the page without requiring any action is what will encourage users to explore the website. To make sure that people do that, you need to provide content that keeps them interested. Thus, place high-level content with ample negative space at the top of the screen, and then increase the content density as the scroll deepens.
Write Crisp Copy
In their book The Elements of Style, Strunk and White advise, “Omit needless words.” This is true for minimalism. Edit your copy to include only the bare minimum needed to adequately explain your message.
Simplify (But Don’t Hide) the Navigation
While simplicity and minimalism aren’t the same, minimalism should be simple. One thing that simplifies the user’s experience is being able to accomplish tasks easily and without distraction. The biggest contributing factor to this kind of simplicity is intuitive navigation. But navigation in a minimalist interface presents a significant challenge: In an attempt to remove all unnecessary elements and streamline the content, designers often hide some or all of the navigation. A menu icon that expands to a full list of items remains a popular design choice, especially in minimal web design and mobile UIs. This often results in lower discoverability of navigation items. Take this website’s hidden navigation:
Compare that to this website’s permanently visible navigation:
Remember that easy navigation is always one of the top goals of web design. If you design minimalist websites, ensure that visitors can find what they need easily.
Incorporate Functional Animation
Like any other element, animation should follow the principles of minimalism: subtle and only what is essential. Good UI animation has a purpose: It is meaningful and functional. For example, you could use animation to save screen space (revealing hidden details on hover). The animation in the example below adds a level of discoverability, making an otherwise mundane task feel a bit more fun.
Use Minimalism for Landing Pages and Portfolios
While the minimalist philosophy behind content-driven design applies to every website, a minimalist aesthetic might not always be appropriate. Minimalism is well suited to portfolio websites and landing pages, like in examples below, which have fairly simple goals and relatively little content.
At the same time, applying minimalism effectively to a more complex website can be much more difficult. A lack of elements can be harmful to a content-rich website (low information density forces the user to scroll more for content). A better option might be to create a minimalist landing page that leads to more detailed pages.
Conclusion
Minimalist websites simplify interfaces by removing unnecessary elements and paring down content that does not support user tasks. What makes such websites inspiring is the combination of usability and great aesthetics: An easily navigable, beautiful website is a powerful vehicle of communication.
Most days, your goal as a developer is to design, develop and program awesome software. However, part of the job is also finding new clients, and you don’t want to be caught off guard by unexpected legal documents that come up while you’re establishing new clients.
The most common legal document you will be asked to sign when working on a website or app is a non-disclosure agreement (NDA). If you’re not sure whether to sign an NDA as a developer, this article will guide you to make an educated decision.
Staying On The Safe Side
Free sample documents and commentary are never a substitute to legal advice, but they can be quite useful to help you get an idea what needs to be included. There are a wide range of starting points for less experienced creative professionals available. Read a related article →
What Is An NDA?
An NDA is a type of contract in which one party, typically called the receiving party, agrees to keep confidential certain information it learns from the other party, typically called the disclosing party. NDAs can also be mutual, whereby each party agrees to keep certain types of shared information confidential. Frequently, an NDA will specify that the project, such as the development of a new app, should not be discussed except with those who have also signed the NDA.
An NDA is very useful for clarifying the expectations of the parties at the outset of the relationship. It also enables the disclosing party to feel comfortable sharing confidential information that is crucial for the project to proceed.
NDAs can also protect trade secrets. A trade secret could be anything from a business method to a customer list. It could even be a special formula that has economic value because it is kept secret from the general public and cannot easily be figured out by third parties based on publicly available information.
Theft of trade secrets is a major concern in both the United States and Europe. In the US, most states have already adopted the Uniform Trade Secrets Act, which defines trade secrets as well as remedies under state law for the theft of such trade secrets. In 2016, the US went one step further with the Defend Trade Secrets Act, which also protects trade secrets under federal law. In Europe, a 2013 study found that more than 20% of companies in the European Union, including many technology companies, have suffered such theft. In 2016, the European Commission proposed new rules to improve fairness and consistency across the EU with regard to access for legal actions for trade secret theft. These rules were approved by the European Council in May 2016. Therefore, in both the US and Europe, there is currently a big push to protect trade secrets and to punish violators more forcefully.
Why Are You Being Asked To Sign An NDA?
If you are a developer, trade secrets such as algorithms, prototypes, designs, drawings and business intelligence might be of critical importance to your client’s business. Your client might even have an invention that it plans to patent.
Today, both the US and Europe use a first-to-file patent system, making it more important than ever to keep inventive information confidential until a patent application is safely on file with the US Patent and Trademark Office or the European Patent Office. Given the value of these types of intellectual property, your client will naturally want to protect them with an NDA.
Unfortunately, the histories of some of today’s most popular apps and websites are littered with examples of people who were damaged because they didn’t have a good NDA in place. Twitter cofounder Noah Glass came up with the name of the ubiquitous platform and did a lot of the early work on it, but he failed to secure NDAs at the outset of the company to protect his work and ideas. He was later forced out. Web app developer Theodore Schroeder claims that investor Ben Cohen stole his ideas about the concept of “boards” and infinite scrolling and shared them with Pinterest CEO Ben Silbermann. However, Schroeder could not prove his case, and it was dismissed. In both of these cases, a well-drafted NDA could have helped to protect these developers.
How Do You Make Sure The NDA Is Fair?
When you are given an NDA, your responsibility is to read it over and make sure it is also fair to you.
When reviewing the clauses, here are five important issues to keep in mind.
Timing
When are you being asked to sign the document, and who is asking you to sign it? Usually, it isn’t reasonable for a potential client to ask you to sign an NDA before your first conversation about the project. You don’t want to agree to something when you have no idea what the project is about or whether you might already have a conflict. However, if you have had a high-level conversation and have a good idea of the project by the time you’ve agreed to work together, then it would be appropriate for them to ask you to sign an NDA.
What Does the NDA Say About the Source of the Confidential Information?
For example, most NDAs would prohibit the receiving party from using the confidential information to develop a separate project or from using it in another party’s work. As a developer, you want the NDA to specifically say that you are prohibited from using the confidential information in such a manner if it was learned from the disclosing party. If you learn the confidential information in another way, such as through an accidental public disclosure or from a different client, you would want the NDA to be flexible enough to permit you to use it in other projects.
Confidential Information Vs. Trade Secrets
Does the NDA distinguish between treatment of confidential information and treatment of trade secrets? A well-drafted NDA will clearly identify how each type of information should be treated. For example, there may be some people in the project with whom you shouldn’t discuss trade secrets. In addition, the NDA should have a limit on the term protecting information that is merely confidential, whereas a savvy client will know that trade secrets need to be protected indefinitely.
Effective Length of Confidentiality
The NDA should clearly specify the term during which the confidential information must be maintained secret. In the fast-paced world of technology, you don’t want to be tied to an inordinately long term, because today’s new app could be tomorrow’s old news. However, as discussed above, trade secrets should always be kept confidential, because disclosure of a trade secret would destroy its value.
Consequences of Breach
In the event that one or both parties breach the agreement, the NDA needs to be clear on the consequences. A breach could lead to legal liability, monetary damage, loss of professional reputation, even a stop-work injunction from a court — not to mention the headache of being caught up in a lawsuit. Both sides should fully understand what they are asking, what is being asked of them, and what the consequences are if one or both parties do not abide by the terms of the NDA.
Red Flags
Now that you understand what an NDA is and some important things to look out for, you will want to make sure that the NDA you are signing conforms to industry standards (and if it doesn’t, ask why not). To provide a little more context, here are six red flags that could indicate you shouldn’t sign.
An Overly Broad Definition of Confidential Information
If alarm bells go off when you read the definition of confidential information, bring it up right away. An overly broad clause could impact your other work. It might be helpful to list items that are explicitly not confidential, including but not limited to publicly available information, information known prior to receiving it from the disclosing party, and information provided from a third party on a non-confidential basis. You also want something to protect you in the event of compelled disclosure, such as a subpoena or government investigation.
Excessive Term of Confidentiality
As discussed, a well-drafted agreement will differentiate between the terms of confidentiality for trade secrets and for confidential information. If it does not, or if the terms otherwise seem excessive, this is a definite red flag, especially in the fast-moving tech industry.
Every Clause Is One-Sided, or No Willingness Is Shown to Negotiate Clauses
It’s not unusual for an NDA to be slanted toward the party that is asking you to sign, especially if they are using an NDA prepared by their lawyers. However, if every clause in the document obviously favors them and they show an unwillingness to negotiate any clauses in the document, this should give you pause. A difficult client who demonstrates trust issues right from the beginning of the business relationship and shows an inability to compromise might be more trouble than they are worth.
Noncompete Clauses That Go Beyond the Scope of the Project
Read non-compete clauses very carefully. Discuss any clause that appears to go beyond the scope of the project and that could impact your work with other clients. Keep in mind that enforcement of non-compete clauses varies based on state law in the US, with some states (such as California) almost refusing to enforce them. In Europe, a company generally must show a reasonable business interest in having a non-compete clause.
Unfair Damages Clause
A good damages clause should clearly address the consequences of a breach of confidentiality. A fairly drafted clause often lists different consequences, depending on whether the breach of the agreement was intentional, negligent or without fault of the breaching party.
NDA Should Not Obligate You to Work on the Project
An NDA is not the same thing as a contract or project agreement. Nothing in the NDA should require you to work on the project. Ideally, you wouldn’t be asked to sign the NDA unless you’ve already agreed to work on the project. If the client does ask you to sign an NDA before you have agreed to work together, make sure the NDA does not obligate you to do the project once it is signed.
What To Do If You Spot A Red Flag
Sometimes a simple discussion with the client about the NDA will help. NDAs are legal documents written by lawyers, and clients themselves very often do not understand the implications of what they are asking developers to sign. Mention to the client that you have carefully reviewed the NDA and have some concerns about it and that some of the clauses seem unnecessary or excessive. Ask if the relevant clauses can be edited or removed, and gauge their response. Remember that you can also consult your own lawyer if you don’t understand something or have additional questions.
Another helpful thing you can do is to compare the NDA with a standard, publicly available NDA in order to show why one clause or another makes you uncomfortable. (Legal Templates offers a legal document builder that bills you after a 14-day trial.) You can also check out other articles that discuss important clauses for more details on any of the clauses discussed above. Comparing your NDA to these can help you better understand it and catch any unusual clauses early on. Finally, if you are truly uncomfortable with the NDA but still want to work with the client, you could ask the client simply to add a confidentiality clause to the contract that describes your working relationship instead.
When Should You Ask The Other Party To Sign An NDA?
If you are in a work-for-hire relationship as a developer, then an NDA is usually not necessary. However, if you feel that your own confidential business information needs to be protected, then a mutual NDA might be a great idea.
What types of information might need to be protected? Some examples include passwords, account numbers and login names; salary information; business methods; future business plans; and customer data. If you think you might be acting as more of a business partner than just a developer and your business ideas might be used in the eventual app or product, this is another time to consider a mutual NDA (and you’ll also want to make sure the contract that clarifies your working relationship satisfies your needs as well).
It’s normal to feel a little intimidated when reviewing a legal document. However, NDAs are a part of life for developers. If you want to work for top clients, you will be asked to sign them. A client who has an idea for the next hot app would be smart to protect it. Keep these tips in mind, and don’t hesitate to ask questions. And remember, when done correctly, an NDA can protect you and the code you write, too.
Bright, colorful leaves, rainy days, Halloween. That’s October — at least if you’re living in the Northern hemisphere. To provide you with some fresh inspiration even when the weather is gray, artists and designers from across the globe once again challenged their creative skills to design beautiful, one-of-a-kind (and this time around also spooky) wallpapers for you to indulge in.
This monthly wallpapers mission has been going on for nine years already, and we are very thankful to everyone who has and still is contributing to it each month anew. The wallpapers in this collection all come in versions with and without a calendar for October 2017 and can be downloaded for free. Happy October!
Please note that:
All images can be clicked on and lead to the preview of the wallpaper,
You can feature your work in our magazine by taking part in our Desktop Wallpaper Calendars series. We are regularly looking for creative designers and artists to be featured on Smashing Magazine. Are you one of them?
Spooky Icons And Halloween Inspiration
Whether or not you celebrate Halloween, there is something magical about that special spooky day. It allows our imagination to unfold and lets us be whatever we want to be — for that one very special day (and night). Spice up your designs with some freebies and spooky designs. Get creative →
“Have you ever wondered if all the little creatures of the animal kingdom celebrate Halloween as humans do? My answer is definitely YES! They do! They use acorns as baskets to collect all the treats, pastry brushes as brooms for the spookiest witches and hats made from the tips set of your pastry bag. So, if you happen to miss something from your kitchen or from your tool box, it may be one of them, trying to get ready for All Hallows’ Eve.” — Designed by Carla Dipasquale from Italy.
“It’s time to bring that monster outside. October is the only month where people easily believe that you are a monster. We wish that you all will celebrate this Halloween with our calendar. So, ready for the Trick or Treat?” — Designed by Color Mean Creative Studio from Dubai.
“It’s the time of the year when people light bonfires and wear costumes to ward off roaming ghosts. It is October and it’s Halloween time!” — Designed by BootstrapDash from India.
“The best part of October is undoubtedly Halloween. And the best part of Halloween is dog owners who never pass up an o-paw-tunity to dress up their pups as something a-dog-able. Why design pugs specifically in costumes? Because no matter how you look at it, pugs are cute in whatever costume you put them in for trick or treating. There’s something about their wrinkly snorting snoots that makes us giggle, and we hope our backgrounds make you smile all month. Happy Pug-o-ween from the punsters at Trillion!” — Designed by Trillion from Summit, NJ.
“The days are colder, but the colors are warmer, and with every step we go further, new earthly architecture reveals itself, making the best of winters’ dawn.” — Designed by Ana Masnikosa from Belgrade, Serbia.
“As it gets colder outside, all I want to do is stay inside with a big pot of tea, eat cookies and read or watch a movie, wrapped in a blanket. Is it just me?” — Designed by Miruna Sfia from Romania.
“Pumpkins, you can see them everywhere you look this month and not only for Halloween but also as autumn decorations.” — Designed by Melissa Bogemans from Belgium.
“8th October is the birthday of one of my favourite horror authors, R.L. Stine. Growing up, I have always loved reading the Goosebumps books, my favourite was ‘Night of the Living Dummy’.” — Designed by Safia Begum from the United Kingdom.
“We all have the potential to create fine things, perhaps wondrous things. However, it’s only the last-minute hurry-burry that makes us realize the gravity of what we are midst. It untangles all the hiccups and urges us to spring up to a different world of fashioning incredible things!” — Designed by Sweans from London.
“The term ‘Hanlu’ literally translates as ‘Cold Dew.’ The cold dew brings brisk mornings and evenings. Eventually the briskness will turn cold, as winter is coming soon. And chrysanthemum is the iconic flower of Cold Dew.” — Designed by Hong,ZI-Qing from Taiwan.
“We are witnessing violence and hatred around the world in the name of nationality, religion, creed, caste, etc. These are what Mahatma Gandhi had stood against. Let us pledge to become the reason of change to bring about a change in the world around us, following what Gandhji left behind.” — Designed by Acodez IT Solutions from Mumbai, India.
“In India, one of the most celebrated festival is Diwali or the Festival of Lights. It’s a five-day celebration that includes delicious food, fireworks, colored sand (Rangoli), lovely earthen lamps (Diya) and lanterns. Diwali is celebrated beyond cultures and religions, embracing all. Its signifies the victory of good over evil and light over darkness. Happy Diwali to everyone!” — Designed by Hemangi Rane from Gainesville, FL.
With grid all of our sizing happens on the container. Once we have created our grid tracks, we can then tell individual items how many tracks to span, but we have an actual grid. We can completely lose row wrappers as grid already has rows. This also means that we can have items span rows too, in exactly the same way as we span columns. Something that has been very difficult to do before now.
Should Grid Be Used For Main Layout And Flexbox For Components?
This myth keeps popping up as people start to learn grid layout. Perhaps it comes from the use of grid systems such as those found in Bootstrap or Foundation where we are concerned with placing items on an overall grid. That is certainly one way to use grid layout. I would however move to thinking about the differences I mentioned in the last section. Ask yourself, is this layout one or two dimensional?
If you can take your component and draw a grid over it, with rows and columns. It is two-dimensional — use grid layout for that.
A grid has strict rows and columns. Changing the size of an item in a cell in a row or column will change the size of the entire track.
If instead, you want individual items to expand within a row, without respecting what happens in the row above. That’s a flex layout.
In this flex layout we want items to arrange themselves by row, and not try to line up as columns.
It doesn’t matter if the item you are trying to lay out is a full page, or a tiny component. What matters is how you want the items inside that layout to distribute space and relate to each other.
Can Grid Track Sizing Be Dictated By Content?
We’ve now seen how, when using Grid Layout, we set up the grid and grid sizing on the container. However it is possible for items inside the grid to dictate track sizing. The key thing to remember here is that a change of size in one cell will change the size all along that track. If you don’t want that to happen, you probably want a single dimensional flex layout.
The simplest way in which we see items changing the sizing of their track is when we use auto, which is the default for tracks created in the implicit grid. An auto sized track will expand to contain all of the content placed into it. In the example below I have a two-column layout, adding more content to the right hand column causes the whole row to expand. The second row is also auto sized, again expanding to contain the content.
We can allow tracks to size within two parameters, for example creating tracks that are at least a minimum size but will still grow to accommodate larger items. We do this with the minmax() function. The first value passed into minmax() being a minimum size for the track, and the maximum the maximum size. Therefore you can form rows that are 200 pixels tall but by setting the maximum as auto ensure that you don’t end up with overflows when there is larger content.
We also have some interesting new sizing keywords, that I’ll be having a proper look at in a future article. These work with grid specifically to allow content to change track sizing, and can be found detailed in the CSS Intrinsic and Extrinsic Sizing module. The keyword min-content for example, when used for grid track sizing will create a track that displays as small as possible when all soft-wrapping opportunities are taken.
In my example below this means the word opportunities. becomes the widest thing and the track shrinks down to fit that.
The opposite happens if you use max-content — you get a track that stretches as large as possible without wrapping. This may lead to overflow situations, in my example I have set the grid to overflow: scroll so the max-content track is causing a scrollbar.
Once again, the key thing to remember is that this is going to happen right across the track. You need to ensure that items in other cells of that track will also absorb that extra space neatly.
Understanding how to size tracks, and how content will change track sizing is probably one of the things that newcomers to grid layout find most confusing. It’s ok to find this takes a little while to understand — we’ve not had anything that behaves like this before. Play with examples, it is the best way to start to understand how things work.
Can I Do A Masonry Layout With Grid?
There is a misconception that grid layout is the same as a Masonry or Pinterest layout. This is generally based on seeing how auto-placement works in grid layout, which at first look seems a bit like Masonry. In this next example I have a layout using auto-placement with grid-auto-flow set to dense. This causes grid to pick items up, take them out of source order and try to backfill gaps in the grid.
However this isn’t really Masonry as we still have a strict grid of rows and columns, and potentially items are taken out of source order. A real Masonry layout would keep things in source order working across the row. Items are pushed up to fill partial spaces left. It’s more like doing flex layout but in both dimensions at once.
a Masonry-style Layout currently needs JavaScript, such as this example using macy.js — macyjs.com. (Large preview)
You can get the look of Masonry with a grid layout by positioning all of your items but the ability to do an auto-placed Masonry layout isn’t there yet. It is something we are thinking about however for future levels of the specification.
How Do I Add Backgrounds And Borders To Grid Areas?
While on the subject of things that grid doesn’t do yet, a common request is to style the backgrounds and borders of the grid areas themselves. Can you add borders and visually display the grid? At the current time this isn’t possible, you need to insert an element and style that, this could be an HTML element but could also be some generated content.
In this next example I have added some generated content to the grid, positioned it using line-based placement and then added a background and border to that area.
To be able to have proper styling of areas of the grid we would need to introduce the concept of grid area pseudo-elements, a special sort of generated content. There is an issue raised regarding this on the CSS WG GitHub site, so you can follow discussion and add your own thoughts.
Spanning To The End Of The Grid
Grid Layout has a concept of the implicit and explicit grid. The Explicit grid is the grid that we define when we use grid-template-rows and grid-template-columns and pass in a track listing. This track listing defines the extent of the explicit grid. The implicit grid is created when we place an item outside of the explicit grid, or when we have more items placed via auto-placement than we have created tracks for.
Tracks created in the implicit grid will be auto sized unless you set a track sizing using grid-auto-rows or grid-auto-columns.
In many cases the implicit and explicit grids behave in the same way, and for many layouts you will find you define columns and then allow the rows be created as an implicit grid. There is a difference however that trips people up, and this is found when you start using negative line numbers to refer to the end line of the grid.
Line -1 is the end of the explicit grid
Grid respects writing mode. In a left to right language, column line 1 is on the left and you can us line -1 to target the right-hand column line. In a right to left language, column line 1 is on the right and -1 will target the left-hand line.
Where people get caught out is that it is only the explicit grid that can count backwards. If you have added rows in the implicit grid and then try to target the end line with -1, you will find that you get the last explicit grid line and not the actual end of your grid.
At the beginning of this article I described how grid is very different to the layout methods that came before it. Due to the limitations of float and flex-based grids, we have needed to become good at calculating percentages in order to do layout and so the first thing that most people do is try and use the same method in their grid layouts. However, before doing so don’t forget our new friend the fr unit. This unit is designed for grid, and works because of the way that grid sets up sizing on the parent element.
The fr unit allows us to distribute a share of available space. It does so by looking at what space is available in the grid container, taking away any space needed for gutters, fixed width items, or content that is defining track sizing and then sharing out the rest according to the proportions we have specified for the tracks. This means that the scenario we have with a floated or flex layout for example, where we have to have flexible gutter tracks so that all of our percentages add up, isn’t the case with grid layout.
In most cases the fr unit is a better choice than percentages. A reason you might choose to use percentage would be where you need a grid layout to match up with other elements using some other layout method and relying on percentage sizing. However if that isn’t the case, see if the fr unit will serve your needs before starting down the route of doing all the maths yourself!
Can I Nest Grids?
A grid item can also become a grid container, in the same way that a flex item can become a flex container. These nested grids however have no relationship to the parent grid, so you can’t use them to line up internal elements with other nested grids.
In a future level of grid layout we may well have a method of creating nested grids that do maintain relationship to the parent grid. This would mean that items other than direct children of the grid could participate in an overall grid layout.
Can I Polyfill Grid Layout?
I’m often asked if there is a way to polyfill grid layout, with people wanting to know if there is a drop in and forget it way to support older browsers.
My advice would be that this isn’t something you want to do. It is likely to create a very slow and janky experience for those browsers already struggling to render modern websites. If you need older browsers to look identical to modern ones then maybe you should reconsider using grid on this project. However in most cases it is possible to use older methods to create a simpler fallback tailored to non-supporting devices without needing to create two completely different sets of CSS. This really needs an article to cover it in detail, so look out for that on Smashing Magazine soon!
Debugging Grid Layouts
As you start to work with grid you will quickly want to be able to see your grid and how the items on it are laid out. I would suggest that you download a copy of Firefox Nightly and use the Grid Inspector in the Firefox DevTools. If you select a grid you can then click the small grid icon — which I like to think of as a waffle — to display the grid.
Firefox have created an excellent tool here, and while Chrome have begun to implement something into Chrome DevTools, right now the Firefox tool is the best in class and it makes working with grid so much easier.
This Is Still New To All Of Us
I know the CSS Grid specification very well, but I’ve only been able to use it in production since March, just like everyone else. As we all move from creating little examples and really start to push the edges of the specification in production work, we will start to find new ways to use grid and of course new problems to solve! I would love to see your own write-ups and demos of the way you are using Grid and other layout methods. I’m also going to be digging into layout issues here at Smashing Magazine over the next few months, so do let us know what you are finding out, and what you would like to know more about.
(This is a sponsored post). What do UX designers do on a daily basis? A lot of things! UX professionals need to communicate design ideas and research findings to a range of audiences. They use deliverables (tangible records of work that has occurred) for that purpose.
The list below contains the most common deliverables produced by UX designers as they craft great experiences for users. For better readability, I’ve combined the deliverables according to UX activities:
If you’d like to create and design your own prototypes a bit more differently, you can download and test Adobe XD, and get started right away.
Obtaining The Best Mobile User Experience Possible
Always remember that design isn’t just for designers — it’s for users. It’s important to treat your work as a continually evolving project, and use data from analytics and user feedback to constantly improve the experience. Read a related article →
Project Assessment
Project assessment is an evaluation process which helps UX designers understand a current state of the product.
Analytics Audit
An analytics audit is a way to reveal which parts of a website or app are causing headaches for users and are reducing conversions. During an analytics audit, an auditor will use a variety of methods, tools and metrics — traffic sources, traffic flows, conversions (and abandonments) hot spots, etc. — to analyze where a product is going wrong (or right). Ultimately, an analytics audit should enable UX designers to know how to boost conversions by making it easier for users to achieve their goals on the website or app.
Numbers provided by an analytics tool on how the user interacts with a product — clicks, user session time, search queries, conversion, etc. — will help UX designers to uncover the unexpected, surfacing behaviors that aren’t explicit in user tests. (Image: Ramotion) (View large version)
Tip: Get into the habit of A/B testing your design changes. Knowing that all of your changes will be A/B tested will give you a tremendous amount of freedom to try new (and potentially risky) things. If they work, you’ll find out almost immediately. Also, you won’t need to worry that some change you’ve made will ruin everything.
Content Audit
Content audit is the process of evaluating information assets on some part or all of an app or website. It could be said that a content audit is a content inventory and evaluation of each page’s content (either qualitative by a person or quantitative using analytics) and/or an assignment of content owners. It involves gathering all of the content on your website or in your app and assessing its relative strengths and weaknesses in order to prioritize your future marketing activities. By auditing, you’ll understand the content much better. You might find things you didn’t know existed, spot duplicated or outdated content, or identify all kinds of relationships in the content. The results of a content audit can be used for a global task (creating a content strategy) or a local task (optimizing a certain page).
Usability testing is a way to see how easy a product is to use by testing it with real users. A usability test report summarizes usability findings in a clear, concise and descriptive way, helping the project team to identify issues and work towards a solution.
Tip: Rank your findings. Every issue that’s discovered through usability testing is not equally important. A usability report could have 5 or 100 findings, depending on the scale of the study, and sometimes it might be overwhelming for a team to go through all of them. That’s why findings should be ranked in terms of severity (low, medium or high). This will help the team identify critical issues exposed by the usability study.
Competitor Assessment
Competitor assessment is an assessment of the strengths and weaknesses of current and potential competitors. Assessing the strengths and weaknesses of your rivals is a critical part of your own UX strategy.
Competitive Analysis Report
An analysis of competitor’s products will map out their existing features in a comparable way. This competitive analysis report helps UX designers to understand industry standards and identify opportunities to innovate in a given area.
A competitive analysis allows designers to assess a competitor’s strengths and weaknesses in a selected marketplace and implement effective strategies to improve a product’s competitive advantage. (Image: yellowpencil) (View large version)
Tip: A useful starting point for identifying strengths and areas for improvement might be user experience heuristics. While competitive analysis isn’t intended to replicate heuristics evaluation, heuristics can be a good starting point because they offer a good structure for presenting information. Heuristics include efficient navigation, clarity of text and labels, consistency, readability, scannability, etc.
Value Proposition
A value proposition is a statement that maps out the key aspects of a product: what it is, who it is for and how it will be used. A value proposition helps the team form consensus around what the product will be.
Tip: Make sure your value proposition is directly associated with key business objectives. By doing this, it will be much easier to have discussions about time and budget for UX activities.
User Research
User research focuses on understanding user behaviors, needs and motivations through observation techniques, task analysis and other feedback methodologies.
Personas
A persona is a fictional character who uses the product in a similar way to a potential user type. Personas make it easier for designers to empathize with users throughout the design process. Personas are a controversial tool in the UX armory: Some UX designers love them, others hate them. Thus, it’s important to understand not just benefits but also downsides of personas before using them in your UX design process.
A persona is a fictional character who highlights the behaviors, needs and motivations of your target users. (Image: xtensio) (View large version)
Tip: The most effective personas are created from in-depth user interviews and observation data of real users. Collect as much information and knowledge about users as possible by interviewing and/or observing a sufficient number of people who represent your target audience.
User Story
A user story is a simple description of a feature told from the perspective of a user of the product. Basically, it’s a very high-level definition of a requirement (at a conceptual level), containing just enough information that the developers can produce a reasonable estimate of the effort required to implement it.
Tip: Use user stories to prevent feature creep. Feature creep is a term that comes up regularly during product design. It refers to the tendency to want to keep adding more features and expanding the scope of a project. Try to refuse to add any feature without a user story that explains why that particular feature matters.
Use Cases
A use case is a written description of how users will perform tasks in the app or website. It outlines, from a user’s point of view, an app or website’s behavior as it responds to a request. Each use case is represented as a sequence of simple steps, beginning with a user’s goal and ending when that goal is fulfilled.
A use case is a list of actions or steps in an event, typically defining the interactions between a user and a system, to achieve a goal. (Image: Slideshare) (View large version)
Tip: Use cases aren’t reserved for the UX phase; they can used for the QA phase as well. Thus, when reviewing the usability of a given product, it’s critical that the QA specialist have the use cases on hand. This will give the QA specialists a set of criteria that will have to have been addressed by the design.
Experience Map
An experience map is a diagram that explores the multiple steps taken by users as they engage with a product. It enables designers to frame the user’s motivations and needs at each step of the journey, designing solutions that are appropriate for each.
A simple experience map reflects one possible path during one scenario. (Image: effectiveui) (View large version)How to create a customer journey map (Watch video)
Tip: The process of creating a customer journey map has to begin with getting to know users. While you can turn to many sources for data about your users, one of the most obvious is website or mobile app analytics. Analytics provide valuable insight into what users are doing on your website or in your app, and this data will help you build compelling cases.
Storyboards (Current)
Storyboards are illustrations that represent shots and that ultimately represent a story. In UX, this story is the series of actions that users would take while using the product. Storyboards help designers to honor the real experiences of the people for whom they are designing.
Smiles and expressions of sadness on human faces have a strong emotional impact. This makes it possible to bring a story to life in the hearts and minds of your audience. (Image: Chelsea Hostetter) (View large version)
Tip: When thinking about storyboarding, most people focus on their ability (or inability) to draw. The good news is that you don’t need to be good at drawing before you start drawing storyboards. What is far more important is the actual story you want to tell. Clearly conveying information is key.
Storyboard frame from Martin Scorsese’s film Goodfellas. (View large version)
Survey
A survey is a quick and inexpensive way to measuring the level of user satisfaction and to collect feedback about the product. While a survey is a great way to collect information from a large number of users, it’s obvious limitation is a lack of qualitative insights — for example, why customers use the product in a certain way.
Tip: Keep the survey short. The temptation when creating a survey is to add more questions. The problem is that it can become painfully long, and people will simply skip questions. If you want to collect more valuable information, you should use a better approach. Keep the survey succinct and run another in a month or two.
Information Architecture
Information architecting is the practice of deciding how to arrange the parts of something to be understandable. For digital products, information architecture results in the creation of navigation, site maps, taxonomies and wireframes.
A site map is a diagram of a website’s pages, organized hierarchically. It makes it easy to visualize the basic structure and navigation of the website.
Tip: If you want to create site map quickly and easily, use the card-sorting method.
Taxonomy
A taxonomy results from an exploration of multiple ways to categorize content and data: articles in a news website, product categories in an e-commerce app, etc. A taxonomy helps designers to define the content structure that supports the user’s and the business’ goals.
Tip: A taxonomy is a living document, and it needs to be retested and updated regularly.
Wireframe
A wireframe is a visual guide that represents a page’s structure, as well as its hierarchy and key elements. Wireframes are useful when UX designers need to discuss ideas with team members and stakeholders, and to assist the work of visual designers and developers.
Wireframes can be presented in the form of sketches:
Tip: Keep wireframes simple, and annotate. The aim of a wireframe is to show the structure of a page’s design — details come later. If you plan to present a wireframe to the team, try to include annotations. Annotations help to create context and quickly deliver key ideas.
Interaction Design
Interaction design (often abbreviated as IxD) is the practice of designing interactive digital products. It’s a process by which designers create engaging user interfaces with logical and thought-out behaviors and actions.
Storyboards (Planned)
Basically, this is the same storyboard that we saw in the section on user research, with just one difference: These storyboards are used to sell design solutions. Designers use them to show the benefits of a proposed solution and to convince stakeholders with it.
Tip: Design a clear outcome. Make sure your storyboard leaves the audience with no doubt about the outcome of the story. If you’re describing an unfavorable situation, end with the full weight of the problem; if you’re presenting a solution, end with the benefits of that solution for your character.
User Flow Diagram
A user flow diagram is a visual representation of the user’s actions to complete tasks within the product. A visualized user flow makes it easier to identify which steps should be improved or redesigned.
User flow helps build a common understanding of each page of your app or website. (Image: Behance) (View large version)
Tip: For many projects in the active design phase, creating user flows might be time-consuming, because drawings will become instantly outdated as screens change. Ryan from Basecamp proposes a simplified version of user flows. This format is really fast to sketch, and it communicates the essentials of what needs to happen.
Prototype
A lot of people use the terms “wireframe” and “prototype” interchangeably, but there’s a significant difference between the two design deliverables: They look different, they communicate different things, and they serve different purposes. While wireframes are similar to architectural blueprints (for example, a building plan), a prototype is a mid- to high-fidelity representation of the final product. The goal of a prototype is to test products (or product ideas) before investing a lot of time and money in the final product.
A prototype gives a taste on how the user will interact with the product. It can be analog:
The most important thing is that the prototype should allow the user to experience content and test the main interactions with the interface in a way similar to how they would with the final product.
Tip: Test prototypes on real devices as much as possible. While an emulator on your desktop might work in some cases, nothing replaces experiencing designs on a real device.
Conclusion
Most likely you are surprised by the number of deliverables mentioned in this article. Rest assured, each project is different and a UX designer wouldn’t need to produce all of them for each project. Also, remember that there is no one-size-fits-all deliverable that will be equally effective for all projects. Each deliverable becomes an effective communication tool in the right context and with the right audience.
This article is part of the UX design series sponsored by Adobe. Adobe XD tool is made for a fast and fluid UX design process, as it lets you go from idea to prototype faster. Design, prototype and share — all in one app.You can check out more inspiring projects created with Adobe XD on Behance, and also visit the Adobe XD blog to stay updated and informed.
Summer might be over, but the memories of the places you’ve visited and the people you’ve met remain. No matter if you explored an exotic country, packed your car for a road trip or took out the hiking boots to discover the nature around you — traveling is a great opportunity to discover new places, gain a fresh view on things, and make lasting experiences.
To keep the essence of summer alive a bit longer, the creative minds at AgenteStudio dedicated an entire icon set to traveling. It includes 60 icons with everything from transportation and equipment to nature, activities and other motifs that are bound to awaken your wanderlust. The icons come in two versions — one monochromatic line art and one with color accents. EPS, AI, SVG and PNG formats are available, so it’s easy to customize the icons to your liking.
Pack your suitcase and let the travel adventure begin! Where will it lead you? (Full preview)
Please note that this icon set is released under a Creative Commons Attribution 4.0 International license. This means that you may modify the size, color, and shape of the icons. Attribution is required, so if you would like to use the icons, please do remember to credit the designers and to link to this article if you want to spread the word in blog posts or anywhere else.
Looking For More Free Icons?
Whether it’s the London Bridge, the Sydney Opera House, or the Taj Mahal, monuments such as these can be used to create logos to adorn websites, travel guides, and even video motion graphics. We’ve got a freebie in vector format ready to be used in any type of medium right away! Download the freebie →
A Closer Look At The Travel Icon Set
Red and blue color accents give the icons a fresh, airtravel-inspired look. (Full preview)Only your imagination is the limit. Why not use the anchor to illustrate “security”, for example, or the lighthouse for staying in touch? (Full preview)
“Summer is one of our favorite seasons. It is the best time to travel, gain new experience, and discover wonderful things. We decided to share our passion for this season with you by creating one more icon set. Our first set was devoted to the most beautiful cities of the world, and this set contains the coolest travel symbols (in our opinion).”
A big thank you to the folks at AgenteStudio for designing this wonderful icon set — we sincerely appreciate your time and efforts! Keep up the fantastic work!
Two years ago, I decided to start a series of short WebGL experiments on Codepen. Earlier this year, I finally found the time to compile them all together on a single website named “Moments of Happiness”. Since its incarnation, I’ve found ways to explore and learn different animation and interaction techniques, which I’ve implemented in these interactive toys.
As you’ll see, the gameplay is very different in each one, but all of the experiments share one principle: The behavior of each character responds programmatically to user input. No precalculated animation — every movement is defined at runtime. Breathing life into these characters with only a few lines of code was the main challenge.
Defining Animations And Explaining Their Purpose
When an animation doesn’t fit a functional purpose, it usually feels awkward or annoying. Well, there are nine logical purposes that can help you validate functional animation. Read a related article →
A Process Of Constraints
Mainly built using three.js and GreenSock libraries, these experiments were completely hand-coded, with no resort to any 3D or animation software.
The process consisted of shaping the characters programmatically, one cube at a time. Most of my effort was spent refining the proportions, the positions and the overall rendering by tweaking the values in the code, and then, finally, moving each part according to user input (moving the mouse, clicking, dragging, etc.).
The advantage of this process isn’t obvious. But it enables me to use only a text editor to create the whole experiment, thereby avoiding all of the struggle of exporting assets and adjusting the properties of the characters using many tools. Taking advantage of the live preview offered by Codepen made the whole process very flexible.
That being said, the process came with its own set of constraints to keep things manageable: The characters had to be built with as few parts as possible; each part consisted of a very low number of vertices; and the animations had to target a limited number of behaviors.
Note:To be clear, this process works for me, but if you are comfortable with a 3D app, you’d better use it to make your models. It’s all about finding the right balance between your own skills to be as effective as possible. In my case, I’m much more efficient when I keep all of my processes in a single tool.
The minimalism required by this process was, in the end, a great opportunity to find the most accurate movement to depict each behavior (comfort, joy, disappointment, etc.).
Every cube and every movement was subject to questioning: Do I really need this one? Does it make the experience better, or is it just the whim of a wannabe character designer?
I ended up with very simple toys, all living in muted and minimalist environments.
The characters are mainly built with cubes — even the fire and smoke were, too!
Animating things programmatically was probably the biggest challenge. How do you build natural and organic movement without any animation software or visual timeline? How do make this animation respond to user input while keeping it natural?
Step 1: Observation
Before starting any of these experiments, I spent some time observing, remembering and conjuring the feelings I wanted to convey.
By the time I made Chill the Lion, petting my dog had become a great source of inspiration; I observed how he closed his eyes for pleasure and exposed his neck to ask for a scratch. Finding the right algorithm to programmatically translate this was a mix of empathy and basic math skills.
Oh, that feeling!
For the “paranoid birds” (below), I remember imitating an uncomfortable-looking guy who had a fleeting look, trying to make the behaviour look convincing by figuring out how much time separated his eye and head movement.
Capturing this awkward movement is just a matter of life experience.
But sometimes, you can’t just rely on your own experience; visual inspiration is sometimes necessary to catch a particular trait. Fortunately, there is Giphy, where you can find subtle expressions of any kind. I also spent a lot of time on YouTube and Vimeo looking for the right movements.
There are as many ways to run as there are reasons to flee.
To achieve this, it was first important to understand how a running cycle works. I looked at some of the most exciting slow-motion GIFs available on Giphy, and I came across this one:
What is interesting to note in this GIF is that a running cycle is not just about moving legs. It is about the whole body, including the smallest parts of it, moving in perfect synchronization. Look at the ears, the mouth and even the tongue participating to enhance the feeling of speed and gravity.
The truth is that there are as many possible running cycles as there are animals and reasons to run. It wouldn’t be a bad idea to have other more accurate references to look at if you want to dig into running cycles. Two useful resources are the “Run Cycle” collection on Pinterest and the awesome “Quadruped Locomotion Tutorial” video.
If you look at these studies, the mechanics behind each running cycle become clearer. Your brain will start to catch on to the relationship between each part of the body, and the sequence and rhythm in the race will reveal a cyclic, repeated and reproducible form.
Now, we need a technical solution to achieve this.
Observing Automatas
Being fascinated by automatas, those mechanical toys that come alive in complex movement as you rotate a single handle, I wanted to try a similar technique and explore a code-based solution that fits my process better than a timeline and keyframes.
The idea is that a looping movement, simple or complex, depends entirely on the progress of one main cycle.
An automaton, a complex animation driven by one rotation of the handle, from Brinquedos Autômatos.
In a running cycle, this entails that each leg, ear, eye, body and head movement is driven by the same main cycle. In some cases, the rotation generated is converted into a horizontal movement and, in other cases, into a vertical one.
When it comes to converting a circular movement into a linear one, trigonometry seems to be the best option.
Step 2: Sharpen Your Weapons, Learn Trigonometry
Don’t run away! The kind of trigonometry needed here is very basic. Most of the formulas will look like this:
x = cos(angle)*distance;y = sin(angle)*distance;
This is basically used to convert the polar coordinates of a point (angle, distance) to Cartesian coordinates (x, y).
By varying the angle, we can make the point rotate around the center.
Converting polar coordinates into Cartesian coordinates
Thanks to trigonometry, we can do much more sophisticated movement, just by playing around with the different values of the formulas. The beauty of this technique is the smoothness you get in the movement.
Here are some examples:
Examples of animations made thanks to trigonometry principles
Now, You Practice
To understand trigonometry, you’ll have to get your hands dirty. Theory without experience is mere intellectual play.
In order to implement some of the formulas above, we’ll need to set up a basic environment. This can be done using canvas, SVG or any library with a graphics API, such as three.js, PixiJS or BabylonJS.
The JavaScript part is a bit longer, but not that complicated:
// Initialize variables.var scene, camera, renderer, WIDTH, HEIGHT;var PI = Math.PI;var angle = 0;var radius = 10;var cube;var cos = Math.cos;var sin = Math.sin;function init(event) { // Get the container that will hold the animation. var container = document.getElementById('world'); // Get window size. HEIGHT = window.innerHeight; WIDTH = window.innerWidth; // Create a three.js scene; set up the camera and the renderer. scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera( 50, WIDTH / HEIGHT, 1, 2000 ); camera.position.z = 100; renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); renderer.setSize(WIDTH, HEIGHT); renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1); container.appendChild(renderer.domElement); // Create the cube. var geom = new THREE.CubeGeometry(16,8,8, 1); var material = new THREE.MeshStandardMaterial({ color: 0x401A07 }); cube = new THREE.Mesh(geom, material); // Add the cube to the scene. scene.add(cube); // Create and add a light source. var globalLight = new THREE.AmbientLight(0xffffff, 1); scene.add(globalLight); // Listen to the window resize. window.addEventListener('resize', handleWindowResize, false); // Start a loop that will render the animation in each frame. loop();}function handleWindowResize() { // If the window is resized, we have to update the camera aspect ratio. HEIGHT = window.innerHeight; WIDTH = window.innerWidth; renderer.setSize(WIDTH, HEIGHT); camera.aspect = WIDTH / HEIGHT; camera.updateProjectionMatrix();}function loop(){ // Call the update function in each frame to update the cube position. update(); // Render the scene in each frame. renderer.render(scene, camera); // Call the loop function in next frame. requestAnimationFrame(loop);}// Initialize the demo when the page is loaded.window.addEventListener('load', init, false);
Here, we have basically created a scene, a camera, a light and a cube. Then, we started a loop to update the position of the cube at each frame.
Now, we need to add the update() function, where we can insert some trigonometry formulas to play around with:
function update(){ // The angle is incremented by 0.1 every frame. Try higher values for faster animation. angle += .1; // Try modifying the angle and/or radius for a different movement. cube.position.x = cos(angle) * radius; cube.position.y = sin(angle) * radius; // You might want to use the same principle on the rotation property of an object. Uncomment the next line to see what happens. //cube.rotation.z = cos(angle) * PI/4; //Or vary the scale. Note that 1 is added as an offset to avoid a negative scale value. //cube.scale.y = 1 + cos(angle) * .5; /* Your turn! You might want to: - comment or uncomment the lines above to try new combinations, - replace cos by sin and vice versa, - replace radius with an other cyclic function. For example : cube.position.x = cos(angle) * (sin(angle) *radius); ... */}
If you feel lost, you can find this setup ready to use on Codepen. Play around with the sine and cosine functions to make the cube move in different ways and to better understand how to take advantage of trigonometry for your animations.
Or you can just move on to the next demo and use it as a starting point to make your own walking or running cycle.
How to Make a Walking or Running Cycle Using Trigonometry
Now, as we learned how to make a cube move with code, by using the same principles, we’re going to make a simple walking cycle, step by step.
We are mostly using the same setup as before, the main difference being that we need more cubes to make the different body parts.
With three.js, it is possible to embed groups of objects inside other groups. For example, we can create a body group that holds the legs, arms and head.
Let’s see how our main character is made:
Hero = function() { // This will be incremented later at each frame and will be used as the rotation angle of the cycle. this.runningCycle = 0; // Create a mesh that will hold the body. this.mesh = new THREE.Group(); this.body = new THREE.Group(); this.mesh.add(this.body); // Create the different parts and add them to the body. var torsoGeom = new THREE.CubeGeometry(8,8,8, 1);// this.torso = new THREE.Mesh(torsoGeom, blueMat); this.torso.position.y = 8; this.torso.castShadow = true; this.body.add(this.torso); var handGeom = new THREE.CubeGeometry(3,3,3, 1); this.handR = new THREE.Mesh(handGeom, brownMat); this.handR.position.z=7; this.handR.position.y=8; this.body.add(this.handR); this.handL = this.handR.clone(); this.handL.position.z = - this.handR.position.z; this.body.add(this.handL); var headGeom = new THREE.CubeGeometry(16,16,16, 1);// this.head = new THREE.Mesh(headGeom, blueMat); this.head.position.y = 21; this.head.castShadow = true; this.body.add(this.head); var legGeom = new THREE.CubeGeometry(8,3,5, 1); this.legR = new THREE.Mesh(legGeom, brownMat); this.legR.position.x = 0; this.legR.position.z = 7; this.legR.position.y = 0; this.legR.castShadow = true; this.body.add(this.legR); this.legL = this.legR.clone(); this.legL.position.z = - this.legR.position.z; this.legL.castShadow = true; this.body.add(this.legL); // Ensure that every part of the body casts and receives shadows. this.body.traverse(function(object) { if (object instanceof THREE.Mesh) { object.castShadow = true; object.receiveShadow = true; } });}
Now we need to add this character to the scene:
function createHero() { hero = new Hero(); scene.add(hero.mesh);}
This is how a simple character could be made with three.js. If you want to learn more about making characters using three.js, read my detailed tutorial on Codrops.
After building this body, we are going to progressively make all of these parts move one by one, until we reach a simple walking cycle.
The whole logic is located in a run function of the Hero object:
Hero.prototype.run = function(){ // Increment the angle. this.runningCycle += .03; var t = this.runningCycle; // Ensure that the angle we will use is between 0 and 2 Pi. t = t % (2*PI); // Amplitude is used as the main radius of the legs movement. var amp = 4; // Update the position and rotation of every part of the body. this.legR.position.x = Math.cos(t) * amp; this.legR.position.y = Math.max (0, - Math.sin(t) * amp); this.legL.position.x = Math.cos(t + PI) * amp; this.legL.position.y = Math.max (0, - Math.sin(t + PI) * amp); if (t<PI){ this.legR.rotation.z = Math.cos(t * 2 + PI/2) * PI/4; this.legL.rotation.z = 0; } else{ this.legR.rotation.z = 0; this.legL.rotation.z = Math.cos(t * 2 + PI/2) * PI/4; } this.torso.position.y = 8 - Math.cos( t * 2 ) * amp * .2; this.torso.rotation.y = -Math.cos( t + PI ) * amp * .05; this.head.position.y = 21 - Math.cos( t * 2 ) * amp * .3; this.head.rotation.x = Math.cos( t ) * amp * .02; this.head.rotation.y = Math.cos( t ) * amp * .01; this.handR.position.x = -Math.cos( t ) * amp; this.handR.rotation.z = -Math.cos( t ) * PI/8; this.handL.position.x = -Math.cos( t + PI) * amp; this.handL.rotation.z = -Math.cos( t + PI) * PI/8;}
These lines of code are the most interesting part, but you can find the full code of the walking cycle on Codepen.
To make it easier to follow, I’ve made the following demo, which breaks down the movement and which highlights the part of the body being moved and the formula used at each step.
Once you are comfortable with sines and cosines, distances and frequencies, it becomes pretty easy to make different cycles, such as running, swimming, flying, even moonwalking.
Your Turn!
I won’t let you go without playing with the bunny.
The Codepen below lets you apply a different angle offset and a different amplitude to each part of the body. You can also modify the speed of the cycle for a more frantic result.
Can you figure out a different running cycle for this guy? Have fun!
One might think that code-based animation leads to unnatural movement. On the contrary, I believe it offers a great opportunity to tweak movement in a very flexible way, which makes it easier to achieve a convincing behavior for your character.
Moments of Happiness is a collection of different experiments, and each had its own challenges. In this article, I’ve detailed the solution used to make a running cycle. On my Codepen page, all of these experiments are available, with the code at your disposal. Feel free to play around and make your own interactive toys.