We have all encountered names that are difficult to pronounce. Having a challenging name myself, I get different pronunciations of my first name, Atif, all the time. In order to solve my own naming problem, I built a Javascript plugin called Vocalizer. In this article, I will introduce what Vocalizer is and a few different ways to use it.
Earlier this year, I redesigned my portfolio website. During this process, I decided to add a feature that educated visitors on how to say my name. One day, I opened the “Voice Memos” app on my iPhone, tapped “Record”, and asked my wife to say my first name. Then, I embedded a small button onto the landing page after my first name. Clicking on that button would play the audio file of my name.
After launching the website, I received a few emails and tweets calling out the audio feature. I even attended a few conferences and meetups where people pronounced my name the right way. A few people expressed interest in implementing the pronunciation feature on their own websites.
Over the next few weeks, I spent time converting my singular implementation into a reusable plugin. Before publicly releasing it, I stumbled upon a company called NameShouts, which is an audio-based pronunciation tool with a repository of over 70,000 name pronunciations. I reached out to them for access to their API, implemented it into my plugin, and open-sourced it.
Vocalizer is a simple, lightweight JavaScript plugin that facilitates the accessibility of difficult to pronounce names. If you’re someone who is unsure of how to say a certain name, Vocalizer shows you how. Or, if you’re someone with an unfamiliar name, Vocalizer shows others how to pronounce it.
The benefit of using NameShouts’ API is that it makes the implementation as quick as possible. In its simplest usage, there are only two steps required to add it to a website.
First, you must include the Javascript library before the closing </body> tag within the code of your website:
Next, all you have to do is wrap your first name in a <span> tag with the specified attributes:
<span data-source="auto">NAME</span>
Note: The latest version of Vocalizer.js is available on CDNJS5, or you can choose to download6 and serve the files from your own server.
As you might have noticed, the data-source attribute is set to auto in the example above. This attribute determines the source of the audio file.
When data-source is set to auto, the library will use the NameShouts API. This option is completely hands-off. Vocalizer will automatically search NameShouts’ name listing and feed in the audio file from their servers.
The alternative option is to manually record and use your own audio file instead of the API. In this scenario, the data-source attribute should be set to the path to your audio file. For example:
There’s a chance that NameShouts’ library does not have the pronunciation for your name, and in that event, you should use your own audio recording or Vocalizer won’t be doing its job!
The actual source code for Vocalizer.js is about eighty lines of code. Let me explain exactly what’s happening under the hood.
When the user wraps his or her name within the <span> tag with the class vocalizer, a Javascript function stores that name into a string:
var name = document.getElementsByClassName('vocalizer'); var names = []; for (var i = 0; i < name.length; i++) { var data_source = name[i].getAttribute("data-source"); names[i] = name[i].innerHTML.toLowerCase().replace(/W/g,'') var request = buildRequests(names[i]); fetchAudio(data_source, request, i); }
We perform this inside of a loop in case there are multiple usages of Vocalizer on the page.
Next, a conditional checks the data-source attribute to see if you’re opting to use the NameShouts API to source the pronunciation or if you’re using your own audio file:
var data_source = name[i].getAttribute("data-source"); if (data_source == 'auto') { /* code for using NameShouts API */ } else { /* code for using your own audio file */ }
The buildRequest() function that we call inside that loop returns the path for the endpoint based on the user’s name.
function buildRequests(n) { return request = 'https://www.nameshouts.com/api/names/'+n; }
From there, we pass the request to the fetchAudio() function and make our xmlHttpRequest to the NameShouts API.
var xhr = new XMLHttpRequest(); xhr.open('GET', request, true); xhr.onload = function (e) { if (xhr.readyState === 4) { if (xhr.status === 200) { var audioPath = JSON.parse(xhr.responseText).message[names[i]][0]["path"]; var audio = new Audio(BASE_URL+audioPath+'.mp3'); audio.playbackRate = 0.75; name[i].addEventListener('click', function() { audio.play(); }, false); } else { console.error(xhr.statusText); } } } xhr.onerror = function (e) { console.error(xhr.statusText); }; xhr.send(null);
NameShouts’ API returns a JSON object that contains the name of the audio file corresponding to your name. From there, we combine NameShouts’ base URL (where the audio files are stored) and the audio file’s path.
In the event the name you are targeting does not exist within the NameShouts API, the plugin will throw an error.
A solution to this situation would be to record and link your own audio file. Setting the data-source attribute to a file path designates your own audio file. In that case, we just create an Audio element based on the file path from data-source:
var audioPath = sourceType; var btn = document.getElementsByClassName('vocalizer'); var audio = new Audio(audioPath, function() { 'Error!'; });
On the front end, an audio button appears after the targeted name. The cosmetics are added with CSS styles on the pseudo-element :after on the <span> tag.
Finally, we add a click event to the audio button:
Vocalizer’s use case can extend beyond just personal websites. I can see blogs and digital news publications begin to adopt the plugin, as well.
Imagine you are browsing news stories on Vox.com10 and you see the name of a political figure or a region of the world with a name in which you are unfamiliar. In that situation, Vocalizer could educate you on its pronunciation. In theory, this would better equip you to discuss these current events and issues. No one wants to sound uninformed.
Another hypothetical use case could be on social media websites. LinkedIn comes to mind for its role in facilitating professional connections.
In an age when people often connect with each other via social media platforms, such as Facebook, Twitter, or LinkedIn, prior to meeting — a tool like Vocalizer could prove useful.
Unbeknownst to me, Facebook recently rolled out a similar feature that automatically generates audio to enunciate names.
It’s nice to see mainstream platforms focus on these types of accessibility features. Unfortunately, there is no guarantee that autogenerated playback will be correct. Facebook’s pronunciation tool mangles my last name.
With every potential solution, there are problems. Vocalizer faces issues of its own. The main issue is that two people with the same name do not always pronounce their name in the same way.
Often, a person’s origin language dictates the pronunciation of their name. For example, the name José in Spanish is pronounced HOH-SEH. In French, the same name is pronounced JOO-ZE. Vocalizer’s current implementation does not cater to these circumstances unless you opt to use a custom recording.
In the last few years, web evangelists have emphasized the importance of web accessibility. Most accessibility functions cater to people with physical and cognitive disabilities. I believe there is a lack of attention in regards to inclusion and cross-cultural accessibility.
Though unintentional, in a way, Vocalizer seeks to enable cultural accessibility. Technology companies continually strive to diversify their workforces. We’re seeing heightened mobility in our professional lives. As a result, multiculturalism is becoming more and more prevalent.
For what it is — I hope Vocalizer helps familiarize people with names from other cultures or ethnicities.
A 2014 study18 found that people with easier-to-pronounce names are deemed “more trustworthy”. I built Vocalizer to solve a problem that has persisted all my life. Now, I hope this will prove useful to others, helping them solve the same problem.
Thanks so much for reading! Please don’t hesitate to tweet to me19 if you have any questions, comments or feedback.
With a couple of days left until New Year’s Eve, it’s just about time to set aside 60 minutes to clean up, sort out and back up your digital footprint, to ensure a good smooth start to 2017. So many little details tend to get forgotten or overlooked every single time, only to get fixed hastily later — but doesn’t it just feel right when everything is in the right place, neatly organized, even if you aren’t a compulsory cleaner or an obsessed perfectionist?
This is not a generic article about unspectacular things like getting to inbox zero or changing the copyright year in your footer (although that’s a good idea!) — we published a detailed checklist1 of all of those details a couple of years ago. Instead, you’ll find below an overview of all of those obscure little things that I forget about every year; so, I decided to gather them all in one place once and for all.
The list below serves as a personal reminder for yours truly, and I thought that it might be useful for you as well. In fact, I set up a yearly reminder on December 28th just to have a couple of days to free the mind for the more important things in life and to start the next year without second thoughts or unresolved issues. Curious? Well, let’s dive in!
If you think something is missing, do please let us know in the comments so that we can put it all together and benefit from it collectively.
Invoices! Who doesn’t occasionally forget to include a payslip in their invoice, basically throwing money out of the window? Extract all the receipts and payslips that have accumulated over the year. You might already be using a dedicated phone app to scan, keep, organize, submit and track your receipts, but you might still have some neglected receipts in your inbox as well.
Collect train, bus and airplane PDF tickets and receipts. You could organize them by destination and add a date stamp in a separate folder (for example, 2016/Dec-London/Receipts).
Collect Uber, Lyft, Gett and other PDF receipts, and save them in those folders as well (for example, 2016/Dec-London/Receipts/Cabs).
Collect PDF receipts for accommodation business expenses (Airbnb, hotels, takeout food), and save them in those folders (for example, 2016/Dec-London/Receipts/Accommodation).
Collect one-time expenses for printed books and eBooks, including any online purchases (such as from Amazon). Don’t forget those handy utilities (Alfred, TextExpander, etc.), mobile apps, as well as your third-party text editor and calendar applications. Again, save them in those folders (for example, 2016/one-time-expenses).
Collect monthly digital expenses as PDF receipts and save them. Think of mobile phone charges, roaming expenses, Internet and TV, GitHub, CodePen, Typekit, Dropbox, Spotify, Slack, BrowserStack, Amazon S3, Adobe Creative Cloud, email services such as MailChimp, Litmus and Campaign Monitor, Flickr, Vimeo, Dribbble, Netflix, CRM tools, Amazon Prime, external services, hosting services and CDNs, usability testing tools, conference and workshop tickets, learning services such as Treehouse and Skillshare, and other monthly service costs2. Not to mention the antivirus software and firewalls that you’re using (and probably paying for monthly as well). Your monthly digital service costs might be higher than you think.
Once you’ve collected all of the receipts, you could set up a little spreadsheet3 to keep track of your expenses (even using the free Google Spreadsheet template4). Your accountant will appreciate it, and you can set up a budget allowance of how much you’d like to spend next year. Knowing where you stand will help you decide how much money to set aside next year, and how you’d like to approach it strategically.
While you’re at it, double-check that all outstanding invoices have been sent and paid, and set up invoice reminders to be sent out automatically either in late December or in early January — for example, with Boomerang for Gmail97.
Throughout the year, we are bombarded with all kinds of notifications — Twitter mentions, Basecamp messages, Trello cards, GitHub updates. Do you really need them? Quite frankly, more often than not, they clutter search, making it more difficult to find things you’re looking for. Delete all notifications that have come in over the years:
While you’re at it, review your notification settings as well. Do you really need hourly notifications, or would daily notifications do just fine? It’s probably a good idea to reduce the frequency of notifications and then see whether you are missing anything — you can always adjust the frequency later.
Delete any other notifications you probably don’t need just by browsing your incoming messages by sender’s address to identify the usual and not-so-usual suspects. You could set up a separate email address just for notifications (for example, notifications@smashingmagazine.com) to prevent them from flooding your primary inbox, so that you can stay focused on emails from colleagues and coworkers.
Also, double check your privacy settings in the applications you are using the most. Review the ads settings, data collection settings and security settings and activities in Gmail10, Facebook11, Twitter12, LinkedIn13, Pinterest14 and Instagram. Double points if you have installed an ad blocker and Privacy Badger15 that blocks spying ads and invisible trackers.
Triple points if you also review authorized apps in Twitter, Facebook and Gmail. You’ve probably tried quite a few apps over the year once and never used them again; no need to grant them permission to access your accounts. Ah, quadruple your points if you look into the front-end performance checklist 201716 as well at this point!
Everybody loves a good ol’ email newsletter. We here are guilty of this as much as any other (albeit lovely) company, and indeed we send Smashing newsletters every now and again (sorry about that!). It might just be time to revisit all of your subscriptions. If you haven’t opened more than five newsletters from a single source, then you might not need them after all. Be brutal and unsubscribe from as many newsletters17 as you can. Depending on how thorough your cleaning preferences are, you might choose to either archive or delete messages received.
Clean up your news alerts and notifications (New York Times, Quartz Daily).
Clean up all out-of-office replies and automatic replies (filter by “out-of-office” and “automatic reply”).
Clean up all event reminders (Meetup.com, Eventbrite, etc.) — just save tickets that you will need for invoicing later, though!
Clean up all form submissions (for example, Wufoo and Typeform). You can track conversations in email or download Excel spreadsheets later if needed.
Clean up all the drafts of messages that you started writing and never finished. If you haven’t sent them out yet, maybe you will never send them at all. Might be a good idea to check the contents of those drafts before deleting them, though — keep the good ideas and delete the unnecessary stuff!
Archive customer support tickets and discussions, as well as emails that are older than a certain threshold. An email that is about three years old might be important enough to look up one day but doesn’t need to be synced across all of your devices.
Clean up all newsletters that you’ve never managed to readand that you won’t be able to read this year. Chances are that if something is important and worth reading, it will bubble up in your conversations with colleagues or on Twitter or Facebook at some point anyway.
Clean up all marketing newsletters from companies you admire and don’t feel like unsubscribing from (MailChimp, Litmus, TED, type foundries, fashion brands etc.).
Delete spam messages and, you know, deleted messages — for good.
Again, just to keep all of these emails away from your primary inbox, it might be a good idea to set up a dedicated account for newsletters and form submissions. Also, set up a filter for all out-of-office replies and automatic replies.
Revisit The Email Signature And Text Shortcuts Link
Double-check that your email signature is up to date, especially your phone number, VAT number and postal address. Is including your mobile phone number in every email absolutely necessary? Maybe your office number would do just fine.
And if you need a bit of inspiration for a tasteful email signature, Kat Neville has got your back with “The Art and Science of the Email Signature20” — an article published seven years ago and still relevant today.
Also, if you’re using shortcuts and text abbreviations, possibly with a tool such as TextExpander, revisit your text snippets and write new ones, to save precious minutes next year.
Unless you’re an extraordinary, super-organized machine, all of your devices have complained to you throughout the year about the lack of space. Well, removing unnecessary email will help, but the main problem usually isn’t just email.
Among all of the apps installed on your phone, tablet, phablet, desktop, laptop, schlaptop, smart TV or anything in between, you are probably not using most of them. If you can’t even remember what an app does, chances are high that you don’t really need it on that device.
You probably have Dropbox21 and Basecamp22 and Trello23 installed on your mobile phone, but how often do you actually access them? Do you need to fully sync your personal folder — usually you’ll have your laptop nearby anyway, right? Delete all the apps you don’t use — maybe you’ve replaced some of the installed ones with the newer, better alternatives, or maybe you tend to use the services on desktop, and the mobile apps aren’t useful anyway?
While finding your way through your apps and looking through the ones you use a lot, revise your privacy and notification settings on mobile — many of them might be slowing you down and draining your battery because they are running in the background. Check what apps are actively updating, refreshing and collecting data about you behind your back in the “Background App Refresh” settings — deactivating them might be a good idea. Also, don’t forget to archive your chat messages and media files sent via WhatsApp and Viber. And — if you’d like to go all the way — delete your accounts on services you don’t use24.
Offload Images And Videos To An External Hard Drive Link
Everybody needs access to a memorable concert video or colorful pictures from a recent trip — I get it. However, when you need space, you need to be reassured that if you delete that great video, you will still be able to find it easily when needed. Having an overloaded phone and running out of space is that one thing that is probably the most annoying (at least to me!) during the year.
Offload all images from your phone and tablet to Dropbox, Google Drive, iCloud, and your laptop or desktop, or just get a dedicated external hard drive and store them there neatly. This goes for movies you haven’t watched yet and TV shows you’re saving for that long-haul intercontinental flight next year.
What happens if your phone drowns or your laptop decides to cease cooperating with you? Be ready in case something happens to your machine and you need it right away. Obviously, you have backed up your phone and your desktop, but what if something fails or you don’t have online access? (Yep, it’s happened to me29 — start watching the first video at 5:55:25).
Prepare a Restore Kit, a folder collecting all your important applications, settings and license codes. Take a close look at all of the applications you use regularly and write them down. You might end up with something like what follows.
Google Maps, WhatsApp, Skype, Foursquare, Instagram, Messenger, Twitter, Facebook, Google Translate, Shopify, Shazam, Spotify, Uber, Lyft, iA Writer, SpeedSmart, Google Authenticator, Opera Mini, Trello, Slack, Airbnb, Adblock, Revolut, banking application
Download the latest versions of these applications, extract the settings or config files from your existing apps, and store them all both locally and on a USB stick. Some services live online, so make sure to reference them (for example, in a plain-text file). Also, don’t forget to extract, encrypt and store the license codes for all of your locally installed applications:
Save encrypted passwords for your Google Chrome and Firefox accounts, so that you don’t need to sync bookmarks, popular search hits, your browsing history or extensions.
Save encrypted credentials to access your primary email account.
Save encrypted PIN and PUK codes for your mobile phone.
Take a screenshot of the arrangement of icons on your desktop, tablet and phone — including the deck and app icons on your phone. They will serve as a point of reference. Sync them via Dropbox — just in case mobile/desktop backup fails at some point in the future (yep, happened to me already as well).
If you put all of these files together in a separate Restore Kit folder, encrypted and stored both locally and on an external USB drive, you’ll be able to set up your working environment and get up and running completely from scratch within half an hour. Bonus points if you use Ninite30 (Windows), Get Mac Apps31, createOSXinstallPkg32 or Homebrew Cask33 (Mac) with a little shell script34 to bundle a handful of installable packages and run them at once one after another automatically. Better to feel safe than sorry!
Just to make sure you don’t leave out something important in your to-do-list or in the “considered-to-be-spam” trench of your inbox, below is a quick checklist of a few things that could be helpful to revisit before diving into the new year:
Install updates you’ve been delaying for weeks.
Check Facebook’s hidden tab, “Message requests,” for people who aren’t your Facebook friends.
Check whether you’re overpaying for mobile, Internet or web services — prices tend to change all the time.
Learn one new keyboard shortcut a week, and define at least one custom text abbreviation a week.
Remove sharing buttons, links and any irrelevant content from the footer.
Review if you need all reserved and parked domains and check when they expire, to set up notifications and extend the license in time.
Write down important phone numbers of relatives, close friends and colleagues — on paper — and store them carefully.
Whenever a loved one mentions something they’d love to have, write it down and order it right away — you’ll find an occasion to give it to them later in the year.
Clean up your desktop before the new year starts — when in doubt, just quickly hide all of the icons on your desktop.
Of course, in a perfect world, we would properly name all files and keep them in properly grouped and marked folders, and we would keep our inbox lean and clean. But more often than not, our data isn’t as neatly organized as we’d love it to be. Set up a cleaning reminder for late December 2017 (and December 2018), maybe even with a link to this article.
Phew! Here we go. Now, this list is quite comprehensive, and taking on the task of cleaning up your digital footprint might be the most tedious task you accomplish this entire year! So, if you had just one hour to get significant improvements, what would you do? Let’s boil it all down to 7 low-hanging fruits.
Think of and collect the receipts for the highest business expenses to send to your accountant early next year.
Double-check that all outstanding invoices have been sent and paid.
Revisit the frequency of notifications for services you use. Delete email notifications. The usual suspects are email, Twitter, Facebook, Trello and Slack.
Review your privacy settings, ads settings, data collection settings and security settings and review authorized apps in Twitter, Facebook, Gmail and others.
Create separate email accounts (and/or filters) for notifications and receipts.
Delete apps that you don’t need on your phone, tablet and desktop.
Revisit your ultimate restore or reboot kit: List essential apps, and collect and sync config and settings files, encrypted license files and the arrangement of icons on your screens. Whenever a loved one mentions something they’d love to have, write it down and order it right away.
Fantastic — you made it! Reward yourself for the last days of the year: uncork and share a bottle of champagne with your family or your friends who managed to get through the checklist just like you, and have a good clean start to 2017! 😉
Not good enough? Well, let us know what’s missing in the comments below!
When designing a landing page to promote a product or service online, you’re ultimately pointing users toward one goal. That goal most often relates to generating business via sales or leads. You may want users to purchase a product immediately, or you may simply want them to sign up for a mailing list. Whatever the goal, you want to ensure that every piece of the user experience works toward fulfilling that goal.
If you don’t yet have goals in mind, start by defining goals. Are you seeking to generate a 10% increase in qualified leads? Are you looking to build sales by 20%? Establishing clear key performance indicators based on what will benefit your business will ultimately help you understand how to properly approach a landing page.
Every goal should tie to a clear business outcome. For example, let’s say you run a software-as-a-service company and have determined that 100 new customers would provide the income you need to meet your revenue goals for the year. If you’ve established proper analytics and are carefully tracking results from your website, you might know that 10% of people who sign up for your mailing list are likely to become paying customers. So, you establish a goal of getting 1,000 new email registrations, which would likely result in your end goal of 100 new customers.
For more on establishing realistic goals and correlating those to online goals, see Avinash Kaushik’s article “Web Analytics 101: Definitions: Goals, Metrics, KPIs, Dimensions, Targets1.” Start with a foundation of being able to track the right data, and build from there to establish metrics you can seek to improve.
Ultimately, the best way to convert a user on a mobile landing page is to provide a clear description of what you’re offering, along with obvious ways to contact you. Be clear in describing what your product does and how you’re solving your target customer’s problems. For instance, “Best vacuum ever” says less than “Breathe more easily with a vacuum that removes 99% of allergens.” Clear contact options include simple, prominent forms and visible phone numbers, so that visitors don’t have to struggle to find out how to reach you.
Get to the point of what you’re selling and show the user how to buy it or contact you. Lack of any distractions, whether from a content or technical standpoint, will help to ensure the page supports your goal of converting users.
Sadly, too often businesses design landing pages without clearly thinking through every context in which users will be viewing the page, including what devices they’ll be on. Creating landing pages with mobile users in mind will help you to focus on how best to convert people on smartphones.
To create a landing page that follows the guidelines we’ll be talking about, you’ll need to pick the right tools to build and host it. If you have the right coding expertise, of course, you could build a page from scratch. However, a number of platforms exist to streamline the creation of landing pages. While the primary purpose of this article is to help you plan the elements and structure of a landing page, here are some brief suggestions of tools to build one.
First, if you already have a hosting service, then WordPress offers an excellent starting point for building a responsive landing page that easily integrates with plugins for forms and other elements. You will find a number of pre-built templates on websites such as ThemeForest2.
Next, several other tools allow you to create and host pages on their platforms. Unbounce3 and Wishpond4 are two popular platforms that enable you not only to build pages but also to implement A/B testing, which we’ll discuss in the last section. Neil Patel has a detailed overview5 of various platforms for landing pages.
A quick Google search will give you thousands of articles recommending various optimal lengths for landing page content. One source says to include at least 500 words6, while another recommends up to 1,000 words7. In reality, the ideal length will depend on what you’re selling and who your audience is, and you can test to determine the length that converts best. However, once the content has been condensed into a mobile format, you’ll need to especially consider how much your users will be willing to read and how far they’ll scroll before you lose the opportunity for a conversion. You can test pages with more or less content (for instance, testing a 500-word page against a 1,000-word page) with an A/B testing tool (discussed in more detail later), monitoring conversion rates to determine whether users are more likely to contact you with more or fewer words to read through.
To identify what content lengths are ideal for your users, you can test to see how long, on average, they’re scrolling through the content. In addition to scrolling, you should factor in how likely people are to engage with content by watching videos, clicking links or filling out forms. Measuring this activity is possible using a heatmapping tool, which we’ll also talk about more in depth later in this article.
In addition, you may start with some assumptions, such as thinking that most mobile users stop at “the fold” of the bottom of their screen. However, several studies8 show that most website visitors will naturally scroll as long as the page provides a user-friendly interface for doing so. For instance, a Time article9 shares that 66% of activity on a page happens below the fold. With this in mind, still keep important content and elements visible as high as possible, but don’t shy away from providing more detail in the copy.
If you’re looking to generate leads for an air-conditioning repair business, people are more likely to want to get right to the point of calling or filling out a form. If someone’s air-conditioning unit is broken on a 90-degree day, they likely won’t want to read a 2,000-word writeup on the inner workings of an air conditioner. They’ll probably be turned off by having to scroll through lengthy text before reaching a section containing contact information. They’ll want to reach right out to someone who can come to fix their unit, and feel assured that the person they contact can arrive quickly to take care of the problem.
However, if you’re selling luxury gold watches for $5,000 each, you’ll likely be better off including a detailed explanation of what sets your watches apart. In this case, you’re targeting a niche wealthy audience who will want to read and visualize details about your products in order to make a purchasing decision. They’re not so likely to make a purchasing decision on a whim with limited information: They’ll want to see pictures showing all angles of the luxury watch, a video about how each watch is handcrafted from the finest-quality materials, and a writeup about a lifetime guarantee.
Whatever the content’s length, take care that individual paragraphs don’t become excessively long when viewed on a mobile device. While a paragraph may stretch to only four lines of text on a desktop screen, the same paragraph might take up ten lines when compressed to a mobile screen size. More frequent paragraphs break will improve legibility.
Whether promoted via paid search or a social media campaign, a landing page should keep a focus on converting users into sales or leads. In the midst of ensuring brand integrity and writing copy to present a product or service, don’t let this main goal fall by the wayside. Especially in the limited amount of space available on a mobile device, you’ll have a brief window in which to grab the user’s attention and, ultimately, to get them to convert.
For a service-related business looking to generate leads, like the air-conditioning repair example, you should feature a phone number prominently across all devices. When users are looking to fix a problem right away, especially when browsing on a phone, they often prefer to place a call for service. Make sure to include click-to-call functionality10 on the mobile version of the page.
For instance, look at the landing page above used for paid search. In a desktop format, the only conversion option is a quote form. However, once the page shrinks to a mobile size, a phone icon appears in the upper-right corner, giving the option to click to call. This change provides a positive conversion focus for mobile, since users searching for insurance from a smartphone would likely want to speak with an agent.
In addition to a phone number, ensure that a form features prominently on mobile. Some responsive design templates that include a form might shrink to the point where the form no longer shows up at a mobile size. This will more than likely result in fewer leads.
Make sure the form’s fields are large enough to be tapped easily with a finger. While a form might work perfectly at a desktop size, the fields might shrink to the point where they’re difficult to select on a phone. For more on designing landing page forms that will aid the conversion process on mobile and not turn off users, see UserTesting’s article covering form usability resources13.
When creating a mobile landing page, a marketing mindset too often leads to one overlooking the user experience. In the process of introducing every possible piece of content or way of presenting a signup form, you could end up turning off users who might have otherwise taken time to read the page and converted. Before launching any campaign, review your landing page on multiple devices with multiple users to determine possible issues to fix. Below are a few examples of potential barriers.
Interstitial forms are a tempting option to “force” users to convert. However, they tend to create a higher number of annoyed users than converted users, especially on mobile. For instance, a Google study14 reveals that an interstitial ad promoting a Google+ app download resulted in 69% of users immediately leaving the page without engaging.
While an interstitial newsletter signup form might be easy to close on desktop, the same popup on a phone might shrink to the point that the “x” is painful to tap. Be careful especially of such conversion tactics that turn into a frustrating barrier for mobile users. Ironically, people can be turned away by the very elements intended to add an extra chance of conversion. For instance, according to a study cited in VentureBeat15, “viewers were 2X as likely to have a negative emotional response to a full page interstitial ad than to a rewarded, opt-in ad.” Also, note that Google recently began penalizing websites16 for some obtrusive interstitial formats.
In addition, think about how responsive design factors into the mobile experience. In theory, the idea of responsively stacking elements to fit a screen size works well. This setup could, however, result in a user having to scroll excessively to get to a form, if you don’t carefully plan out how elements will stack at a mobile size. A templated website might stack a form at the bottom, but you should readjust the layout to place the form higher on the page or include a clear “Contact” button that scrolls with the user’s activity and leads directly to the form when clicked.
Also, think about how text will look when compressed to a mobile size. Will it be too small? Does the color allow for easy legibility? For instance, the white text for the page below looks fine over a dark background at a large size but blends with the light-blue background at a mobile size. However, on a positive note, see how the form fields go from four to one, making the process of completing the form simpler on mobile.
Additionally, don’t keep the user captive on the landing page. While you do want to focus on conversion and don’t necessarily need your website’s full navigation bar, you also don’t want to frustrate a user who’s looking for more information about your brand. Make your logo clickable back to your primary website, or provide footer links back to it.
To better identify what elements of the website are and aren’t working, test the experience of the website on mobile devices. Enlist people using different phone models across both Android and iOS, either finding people you know or using a website such as UserTesting23.
With the launch of a page, you can A/B test the placement of elements on the website using a tool such as Optimizely24 or Google Analytics’ free Content Experiments25. For instance, in the case of pricier products sold to a niche audience, you might want to test if these people will indeed respond to a form they see immediately or if they will need to read through content and view imagery before deciding to convert. For more ideas and tips, see Shopify’s “Beginner’s Guide to Simple A/B Testing26.”
In addition, install a heatmapping tool such as Crazy Egg27 or Hotjar28 to measure clicking and scrolling activity via a tracking script inserted in your website. This data will allow you to look more closely at how people are scrolling and what elements of the page they’re interacting with. In this way, you can determine how far into the content the average user is likely to read, as well as what buttons and form configurations are likely to produce a response.
Be sure to look at heatmaps specific to mobile. While desktop heatmaps might show no issues, mobile might show a different story. Of course, data will vary from website to website, but you might find that a larger percentage of your desktop users scroll through an entire page than mobile users (or vice versa). Or you might find that desktop users are more inclined to fill out a lengthier form, whereas mobile users fill out the first two fields and then drop out due to faulty functionality on mobile. In the example below, see how users scroll through more of the content on desktop than on mobile. Based on this data, the developer would be wise to take out some of the lengthy content on the mobile version of the page.
In addition, use Google Analytics to review mobile performance on your landing page. Segment by device when looking at the data to identify specific issues with mobile use. For instance, in the example shown below, we’ve selected a specific landing page under the “Behavior” → “Landing Pages” report, and used the “Secondary Dimension” dropdown menu to break out performance by device category. Here, we can see that mobile sessions resulted in a significantly lower conversion rate than desktop (0.65% versus 2.35%), indicating a potential flag of a poor user experience.
While page-loading speed is crucial for any website on any device, you should especially consider how quickly a page appears to load on mobile. One extra second of time spent waiting for an image to load could mean that an impatient mobile user gives up. In order to keep the user’s attention, make sure that resources load as quickly as possible in the user’s eyes.
One useful solution involves preloading resources on a landing page. You can use HTML5 prefetch35 to fetch some assets that will be used on a page that appears when the user clicks a call-to-action button on a landing page. In addition, you could also dynamically inject resource hints that tell the browser to download the required resources ahead of time. For more on this topic, see Denys Mishunov’s series on “Why Performance Matters36” (including part 237 and part 338) and CSS-Tricks’ article on prefetching39.
Here’s a recap of what to keep in mind when designing a landing page:
Define your goals to determine what to say on the page and what action you want users to take.
Describe your product or service as concisely as possible to grab the attention of users.
Break content into brief, readable paragraphs.
Exclude distracting elements, such as large navigation bars and excessive external links.
If appropriate, show imagery and/or video related to what you’re selling.
Place conversion elements such as forms and phone numbers in highly visible sections.
Test landing-page performance via analytics, heatmapping and A/B testing tools to determine changes to content length and page elements to include.
When building a landing page for any online campaign, take special care to consider the mobile experience. Review the presentation of content, as well as the prominence of contact information. Solicit the opinions of multiple users to identify issues, and test with analytics data to determine usability. With a clear plan in place for mobile, you’ll better convert users coming from multiple devices.
Welcome to the last reading list of the year. I’m happy to still have you as a reader and very grateful to all the people who value and support my work. I hope you’ll be on vacation for the upcoming days or can relax a bit from your daily work. Remind to take care of yourself, and see you next year!
Vitaly Friedman put together a great Front-End Performance Checklist 20174 for your next projects, with PDF and Apple Pages files so you can tick it off point by point.
We like to say “2016 was the worst”, and I don’t like this. Angus Hervey shares 99 good news stories that we probably didn’t hear about in 201616. Don’t let you be fooled by the all negative news and instead embrace the good things that happened as well. Despite some bad news, 2016 was quite a good year. Enjoy its last days!
Human interactions are incredibly fascinating if you take a close look at them — the social awkwardness, the communication styles, the way knowledge is transferred, the way stories are told and trust is built.
But what happens when a machine evokes the same response?
Conversational interfaces have become the new hotness in UX design. Google is about to release a new virtual assistant chatbot1; Facebook has already launched the updated Messenger platform with chatbots2; and Microsoft went as far as to claim that the operating system of the future isn’t Windows, but “conversation as a platform.”
What all of the big industry players have already figured out is that advances in artificial intelligence (AI) can solve a very important UX problem: making faceless branded websites and apps feel really personal.
Chatbots can create a more genuine, custom-tailored experience, one that could be compared to the experience in a store — you get a smile from the salesperson, some chit chat and a friendly wink, which makes the whole buying experience more personal and pleasant. For brands, this represents an opportunity to extend and manage relationships with their customers and to go beyond being “just a product.”
However, building a genuinely helpful and attractive chatbot is still a challenge from a UX standpoint. Though we now have brilliant machine learning, which advances AI and natural-language processing (NLP) technologies, we are still somewhat limited in the type of helper we can create, and we need to force the most out of what we have. Matt Schlicht, founder of the Chatbots Magazine has created a very comprehensive guide5 summarizing the current state of the chatbot ecosystem and opportunities for designers.
Recently, I worked on the chatbot project for Alty6 — a Facebook messenger bot to chit chat with potential customers, introduce them to the company and services offered, and send out email inquiries.
The experience was relatively new and challenging. Unlike the standard graphical user interface (GUI), the app needed to work seamlessly with little user input, provide new value by leveraging stored information, and anticipate users needs. Standard patterns and flows don’t really work in conversational design, or else they need significant readjustment. Check Bot UI Kit9 for Messenger platform, courtesy of Mockuuups and Botframe10 – a simple prototyping tool for emulating conversations developed by Alsadir Monk11 to get a better idea of the common flows on this platform.
As already stated the first challenge you are likely to encounter is that you have little control over the application’s appearance. You don’t need to fuss over typography, layouts or styling too much. If you are building a voice-control chatbot, it won’t even have a visual side! Hence, ditch most of the standard tools and power up your toolkit with new useful ones.
For our project, we opted for the simplest tool — Chatfuel12, a free, intuitive bot builder for Facebook Manager with a drag-and-drop interface and hardly any coding required.
However, if you plan to build a more advanced bot, it’s worth looking into the following tools:
Twine13 This non-linear text editor creates text scripts and message sequences for your dialogs.
Wit14 This indispensable tool will help you convert voice and text commands into actions. Dozens of handy commands have been created by the community, and you can add custom ones.
Botkit15 Howdy’s Botkit offers a handy set of commands and ready-made code for you to build your first Slack chatbot.
Api.ai16 Recently acquired by Google, this robust and comprehensive platform will help you build any type of conversational UX interface.
Botwiki17 This wiki answers all of the common chatbot questions.
Few of the standard controls or styles we use in standard apps apply to conversational design.
Conversational design completely changes the way users interact with an app. Typically, when a user opens a new iOS app, they will see some familiar elements, such as a menu, a panel to log in or create an account, buttons and so on — the elements they already know how to interact with based on common schemas18.
However, the first encounter with a chatbot is less conventional. The user will be staring at a blank screen, lost in assumptions about what to do next or how to interact with the app. They face two simple problems:
“I have no idea what I’m supposed to do.”
“What exactly can this thing do for me?”
Chatbots don’t seem intuitive for most users yet. Hence, your first task is to prompt the user on what’s about to happen next. Start with a quick introduction and a straightforward call to action, something like:
Keep it short and simple. Invite users to experience one quick benefit of your app and to enjoy the result immediately.
In the case of Alty’s bot, we opted to include buttons within the conversation for a few key reasons:
Typing on the go could be cumbersome for some users, and chatbots are not always smart enough to detect typos (although we’ll talk about dealing with that later on).
Buttons can hint to users about what kind of questions the bot can answer and what actions it can perform.
You want your robot to seem like a wizard, rather than an obstacle, right?
One of the most challenging parts about designing a chatbot is to make the conversation flow as naturally and efficiently as possible. However, human interaction is typically messy and non-linear. Here are some tips for optimizing the app’s performance.
Teach Your Bot To Distinguish Between Different Types of Questions Link
Create the initial scope of questions that your bot will be capable of processing and answering efficiently. You can use a great library named qTypes21, which has over 40 sub-classifications for how questions should be answered. qType indicates the type of reply the user expects, and qSubType indicates the question’s format:
CH
With alternative choice question, the bot is asked to pick between two alternatives (for example, “Is this shirt red or green?”).
WH
These are questions starting with who, what, when, where or why.
YN
These are yes or no questions (for example, “Do you have a dog?”).
TG
A tag question is not an actual question, but rather an option to keep the conversation flowing (for example, “This beach is lovely, isn’t it?”).
When your bot receives one of the standard questions, it can produce more accurate replies based on the data from the library:
Avoid asking rhetorical questions, because most users tend to respond to them anyway, even if the chatbot is just being polite.
Now that your bot is capable of understanding questions, the next challenge is to teach the app to render appropriate commands in return.
A standard GUI allows you to refine inputted data easily when processing it. Is this email address valid? Is this username available? Is this phone number valid? You can easily restrict and refine inputted data before processing it.
Yet, in conversational design, things get a bit more complicated. The user is free to say or type whatever they’d like; hence, you need to be smart when constructing your questions and processing the answers.
Offer hints. Avoid open-ended questions whenever possible because they usually result into more confusion. Instead, prompt for the kind of answer you expect. For example:
What kind of case study would you like to see? We have ones for travel, social networking, design and personal finance apps.
Or you could present the information according to the format of the platform you are building on — for example, lists in the case of Facebook Messenger:
Also, confirm. If the answer is valid, repeat it to ensure that everything is correct, and then move on to the next question:
Got it. Travel apps. And what budget do you have in mind?
Or suggest what went wrong. If the inputted data isn’t valid, explain again what kind of answer you need. Ideally, distinguish between answers that you don’t understand and answers that are fine but that you can’t accept:
Don’t forget that users are talking to your app. They may use different words to describe the same thing — for example, “Thu,” “Thursday,” “tomorrow” or a word with a typo. You could either ask them to confirm their entry or focus on creating more advanced message sequences for your chatbot.
You can refine the inputted data by running it through Normalizer27, a library that converts UK and Canadian spelling to US English, explains common abbreviations and fixes over 4,000 misspelled words.
Wait for critical inputs. In some cases, you’ll need the user to input some essential information that you cannot proceed without. In standard GUIs, the problem is usually solved with a popup modal window that blocks access to everything until the user completes the task: “Did you validate your email address?,” with the window prompting “Yes” or “No.”
However, in conversational design, you should tackle this issue in a slightly different manner. This kind of a loop can get rather annoying with a robot, so make sure to explain the exact action you require and why you need it so critically. Prepare a few conversational snippets for this purpose to keep the chatbot from getting repetitive:
In general, think twice about whether certain information is critical in order to proceed. Whenever possible, make an educated guess, or ask for the same information again during a subsequent step.
Another option is to use buttons and pre-suggested texts that users can choose from both when asking questions and providing the replies. Buttons should improve the overall quality of user inputs, however, they may slightly reduce the engagement factor. So it’s best to use them only when you need to receive the essential data for proceeding.
As AI technology advances, it may become easier to train bots to make certain responses and to teach them to second-guess the user’s intention based on previous interactions stored in the database. Yet the majority of chatbots today don’t have fancy AI brains to respond to users; hence, for a better UX, you’ll need to tackle this job yourself.
The designer should think like a copywriter when developing a chatbot. The content and the dialog will define your product’s style. The best apps are usually those that feature a fun conversational manner of speech. Hence, focus on the following:
Follow the same user flow as you would if you were actually speaking to a person.
The bot shouldn’t sound too clever, using complicated grammar or language structures. Keep it simple and be concise.
Don’t use gender-specific pronouns, because you never know who’s on the other side of the conversation.
Prepare a set of slightly different canned replies to make the conversation more human-like.
Add help messages and suggestions for when the user feels lost.
Write witty replies for unsupported topics, so that the bot doesn’t look dumb.
Standard GUIs usually show all of the features available on the screen at once. The user can hover over icons, click buttons and access the menu to see what the app is capable of doing.
Interacting with a chatbot, however, can seem like the user is speaking into the void. Hence, hint at each next step, and gradually highlight unfamiliar features. Let’s explore what this means.
After receiving the initial command from the user, explain what’s about to happen next and what the robot will do to complete the task. Suggest the next possible steps and/or link to the FAQ page or user manual.
Unlock additional features after the first successful interaction. Disable “training mode,” and start suggesting additional features and more advanced tips. Base those features and tips on the user’s history and previously inputted data.
Prompt the user about new things to do. For instance, proactively suggest some other cool features of your robot:
Hey, you have a party coming up! Do you want me to order 5 large pizzas?
Conversational agility is one of the key strengths of Taco bot, for instance. The company used Wit.ai to power different conversation scenarios and even crack some jokes. The platform’s natural language processing technology, which is also now used to power Facebook’s M Virtual Assistant33, allows the bot to render different ordering styles. For instance, “Can I have a burrito?”, “Buritto, please”, and even hilariously respond to “I’m drunk” request, which triggers the “Ok. A cup of water added to your order” reply. Additionally, users can type a one-line comment like “sans cheese” and the bot will understand that the information refers to the previously ordered burrito.
However, if the chatbot initiates the conversation, make sure it gives relevant suggestions because you don’t want to appear like an obnoxious spammer, right?
Speech commands are becoming a thing with Siri and Google Now, yet developing such bots obviously takes human and material resources. Even the most powerful neural networks that are responsible for speech recognition are rather hard to train at the moment. The most common challenge is that, while small errors are simple enough to eliminate, the larger recurring ones can become even larger due to multiplication, as Andrew Gibiansky points out34.
For instance, if a user with an accent pronounces Apple as Eupple, the network might remember the command this way. Homophones are another major challenge for speech recognition; words like “flower” and “flour” sound identical, and understanding the right context might be hard.
Hence, if your aim is to build a simple chatbot, opting for speech commands might not be your best bet at the moment, unless you are ready to invest heavily in the architecture and advanced machine-learning technology stack.
While chatbots can be a great tool for creating more personalized customer experience, conversational design still have certain limitations. As Mariya Yao pointed out, there are clear cases when a conversation can help or hurt the UX35.
Before building a chatbot for your business, you should clearly define its purpose and the exact value it could bring to the user. Teach the bot to do one thing extremely good, such as delivering weather forecasts or introducing the company’s scope of service before experimenting further with more advanced features. That’s the key lesson we learned when developing the April bot based on user feedback.
Are you using progressive booting already? What about tree-shaking and code-splitting in React and Angular? Have you set up Brotli or Zopfli compression, OCSP stapling and HPACK compression? Also, how about resource hints, client hints and CSS containment — not to mention IPv6, HTTP/2 and service workers?
Back in the day, performance was often a mere afterthought. Often deferred till the very end of the project, it would boil down to minification, concatenation, asset optimization and potentially a few fine adjustments on the server’s config file. Looking back now, things seem to have changed quite significantly.
Performance isn’t just a technical concern: It matters, and when baking it into the workflow, design decisions have to be informed by their performance implications. Performance has to be measured, monitored and refined continually, and the growing complexity of the web poses new challenges that make it hard to keep track of metrics, because metrics will vary significantly depending on the device, browser, protocol, network type and latency (CDNs, ISPs, caches, proxies, firewalls, load balancers and servers all play a role in performance).
So, if we created an overview of all the things we have to keep in mind when improving performance — from the very start of the process until the final release of the website — what would that list look like? Below you’ll find a (hopefully unbiased and objective) front-end performance checklist for 2017 — an overview of the issues you might need to consider to ensure that your response times are fast and your website smooth.
Micro-optimizations are great for keeping a performance on track, but it’s critical to have clearly defined targets in mind — measurable goals that would influence any decisions made throughout the process. There are a couple of different models, and the ones discussed below are quite opinionated — just make sure to set your own priorities early on.
According to psychological research3, if you want users to feel that your website is faster than any other website, you need to be at least 20% faster. Full-page loading time isn’t as relevant as metrics such as start rendering time, the first meaningful paint4 (i.e. the time required for a page to display its primary content) and the time to interactive5 (the time at which a page — and primarily a single-page application — appears to be ready enough that a user can interact with it).
Measure start rendering (with WebPagetest1686) and first meaningful paint times (with Lighthouse1727) on a Moto G, a mid-range Samsung device and a good middle-of-the-road device like a Nexus 4, preferably in an open device lab8 — on regular 3G, 4G and Wi-Fi connections.
Look at your analytics to see what your users are on. You can then mimic the 90th percentile’s experience for testing. Collect data, set up a spreadsheet10, shave off 20%, and set up your goals (i.e. performance budgets11) this way. Now you have something measurable to test against. If you’re keeping the budget in mind and trying to ship down just the minimal script to get a quick time-to-interactive value, then you’re on a reasonable path.
Share the checklist with your colleagues. Make sure that the checklist is familiar to every member of your team to avoid misunderstandings down the line. Every decision has performance implications, and the project would hugely benefit from front-end developers being actively involved when the concept, UX and visual design are decided on. Map design decisions against performance budget and the priorities defined in the checklist.
100-millisecond response time, 60 frames per second.
The RAIL performance model14 gives you healthy targets: Do your best to provide feedback in less than 100 milliseconds after initial input. To allow for <100 milliseconds response, the page must yield control back to main thread at latest after every <50 milliseconds. For high pressure points like animation, it’s best to do nothing else where you can and the absolute minimum where you can’t.
Also, each frame of animation should be completed in less than 16 milliseconds, thereby achieving 60 frames per second (1 second ÷ 60 = 16.6 milliseconds) — preferably under 10 milliseconds. Because the browser needs time to paint the new frame to the screen your code should finish executing before hitting the 16.6 milliseconds mark. Be optimistic15 and use the idle time wisely. Obviously, these targets apply to runtime performance, rather than loading performance.
First meaningful paint under 1.25 seconds, SpeedIndex under 1000.
Although it might be very difficult to achieve, your ultimate goal should be a start rendering time under 1 second and a SpeedIndex16 value under 1000 (on a fast connection). For the first meaningful paint, count on 1250 milliseconds at most. For mobile, a start rendering time under 3 seconds for 3G on a mobile device is acceptable17. Being slightly above that is fine, but push to get these values as low as possible.
Don’t pay much attention to what’s supposedly cool these days. Stick to your environment for building, be it Grunt, Gulp, Webpack, PostCSS or a combination of tools. As long as you are getting results fast and you have no issues maintaining your build process, you’re doing just fine.
Progressive enhancement.
Keeping progressive enhancement18 as the guiding principle of your front-end architecture and deployment is a safe bet. Design and build the core experience first, and then enhance the experience with advanced features for capable browsers, creating resilient19 experiences. If your website runs fast on a slow machine with a poor screen in a poor browser on a suboptimal network, then it will only run faster on a fast machine with a good browser on a decent network.
Angular, React, Ember and co.
Favor a framework that enables server-side rendering. Be sure to measure boot times in server- and client-rendered modes on mobile devices before settling on a framework (because changing that afterwards, due to performance issues, can be extremely hard). If you do use a JavaScript framework, make sure your choice is informed20 and well considered21. Different frameworks will have different effects on performance and will require different strategies of optimization, so you have to clearly understand all of the nuts and bolts of the framework you’ll be relying on. When building a web app, look into the PRPL pattern22 and application shell architecture23.
AMP or Instant Articles?
Depending on the priorities and strategy of your organization, you might want to consider using Google’s AMP27 or Facebook’s Instant Articles28. You can achieve good performance without them, but AMP does provide a solid performance framework with a free content delivery network (CDN), while Instant Articles will boost your performance on Facebook. You could build progressive web AMPs29, too.
Choose your CDN wisely.
Depending on how much dynamic data you have, you might be able to “outsource” some part of the content to a static site generator30, pushing it to a CDN and serving a static version from it, thus avoiding database requests. You could even choose a static-hosting platform31 based on a CDN, enriching your pages with interactive components as enhancements (JAMStack32).
Notice that CDNs can serve (and offload) dynamic content as well? So, restricting your CDN to static assets is not necessary. Double-check whether your CDN performs content compression and conversion, smart HTTP/2 delivery, edge-side includes, which assemble static and dynamic parts of pages at the CDN’s edge (i.e. the server closest to the user), and other tasks.
It’s a good idea to know what you are dealing with first. Run an inventory of all of your assets (JavaScript, images, fonts, third-party scripts and “expensive” modules on the page, such as carousels, complex infographics and multimedia content), and break them down in groups.
Set up a spreadsheet. Define the basic core experience for legacy browsers (i.e. fully accessible core content), the enhanced experience for capable browsers (i.e. the enriched, full experience) and the extras (assets that aren’t absolutely required and can be lazy-loaded, such as web fonts, unnecessary styles, carousel scripts, video players, social media buttons, large images). We published an article on “Improving Smashing Magazine’s Performance33,” which describes this approach in detail.
Use the “cutting-the-mustard” technique.
Use the cutting-the-mustard technique34 to send the core experience to legacy browsers and an enhanced experience to modern browsers. Be strict in loading your assets: Load the core experience immediately, the enhancements on DomContentLoaded and the extras on the load event.
Note that the technique deduces device capability from browser version, which is no longer something we can do these days. For example, cheap Android phones in developing countries mostly run Chrome and will cut the mustard despite their limited memory and CPU capabilities. Beware that, while we don’t really have an alternative, use of the technique has become more limited recently.
Consider micro-optimization and progressive booting.
In some apps, you might need some time to initialize the app before you can render the page. Display skeleton screens35 instead of loading indicators. Look for modules and techniques to speed up the initial rendering time (for example, tree-shaking36 and code-splitting37), because most performance issues come from the initial parsing time to bootstrap the app. Also, use an ahead-of-time compiler38 to offload some of the client-side rendering39 to the server40 and, hence, output usable results quickly. Finally, consider using Optimize.js41 for faster initial loading by wrapping eagerly invoked functions (it might not be necessary42 any longer, though).
Client-side rendering or server-side rendering? In both scenarios, our goal should be to set up progressive booting45: Use server-side rendering to get a quick first meaningful paint, but also include some minimal JavaScript to keep the time-to-interactive close to the first meaningful paint. We can then, either on demand or as time allows, boot non-essential parts of the app. Unfortunately, as Paul Lewis noticed46, frameworks typically have no concept of priority that can be surfaced to developers, and hence progressive booting is difficult to implement with most libraries and frameworks. If you have the time and resources, use this strategy to ultimately boost performance.
Are HTTP cache headers set properly?
Double-check that expires, cache-control, max-age and other HTTP cache headers have been set properly. In general, resources should be cacheable either for a very short time (if they are likely to change) or indefinitely (if they are static) — you can just change their version in the URL when needed.
Limit third-party libraries, and load JavaScript asynchronously.
When the user requests a page, the browser fetches the HTML and constructs the DOM, then fetches the CSS and constructs the CSSOM, and then generates a rendering tree by matching the DOM and CSSOM. If any JavaScript needs to be resolved, the browser won’t start rendering the page until it’s resolved, thus delaying rendering. As developers, we have to explicitly tell the browser not to wait and to start rendering the page. The way to do this for scripts is with the defer and async attributes in HTML.
As far as possible, use responsive images56 with srcset, sizes and the <picture> element. While you’re at it, you could also make use of the WebP format57 by serving WebP images with the <picture> element and a JPEG fallback (see Andreas Bovens’ code snippet58) or by using content negotiation (using Accept headers). Sketch natively supports WebP, and WebP images can be exported from Photoshop using a WebP plugin for Photoshop59. Other options are available60, too.
You can also use client hints63, which are now gaining browser support64. Not enough resources to bake in sophisticated markup for responsive images? Use the Responsive Image Breakpoints Generator6562 or a service such as Cloudinary66 to automate image optimization. Also, in many cases, using srcset and sizes alone will reap significant benefits. On Smashing Magazine, we use the postfix -opt for image names — for example, brotli-compression-opt.png; whenever an image contains that postfix, everybody on the team knows that it’s been optimized.
Take image optimization to the next level.
When you’re working on a landing page on which it’s critical that a particular image loads blazingly fast, make sure that JPEGs are progressive and compressed with mozJPEG67 (which improves the start rendering time by manipulating scan levels), Pingo68 for PNG, Lossy GIF69 for GIF and SVGOMG70 for SVG. Blur out unnecessary parts of the image (by applying a Gaussian blur filter to them) to reduce the file size, and eventually you might even start to remove colors or make a picture black and white to reduce the size further. For background images, exporting photos from Photoshop with 0 to 10% quality can be absolutely acceptable as well.
Chances are high that the web fonts you are serving include glyphs and extra features that aren’t being used. You can ask your type foundry to subset web fonts or subset them yourself75 if you are using open-source fonts (for example, by including only Latin with some special accent glyphs) to minimize their file sizes. WOFF2 support76 is great, and you can use WOFF and OTF as fallbacks for browsers that don’t support it. Also, choose one of the strategies from Zach Leatherman’s “Comprehensive Guide to Font-Loading Strategies8077,” and use a service worker cache to cache fonts persistently. Need a quick win? Pixel Ambacht has a quick tutorial and case study78 to get your fonts in order.
To ensure that browsers start rendering your page as quickly as possible, it’s become a common practice85 to collect all of the CSS required to start rendering the first visible portion of the page (known as “critical CSS” or “above-the-fold CSS”) and add it inline in the <head> of the page, thus reducing roundtrips. Due to the limited size of packages exchanged during the slow start phase, your budget for critical CSS is around 14 KB. If you go beyond that, the browser will need addition roundtrips to fetch more styles. CriticalCSS86 and Critical87 enable you to do just that. You might need to do it for every template you’re using. If possible, consider using the conditional inlining approach88 used by the Filament Group.
With HTTP/2, critical CSS could be stored in a separate CSS file and delivered via a server push without bloating the HTML. The catch is that server pushing isn’t supported consistently and has some caching issues (see slide 114 onwards of Hooman Beheshti’s presentation89). The effect could, in fact, be negative90 and bloat the network buffers, preventing genuine frames in the document from being delivered. Server pushing is much more effective on warm connections91 due to the TCP slow start. So, you might need to create a cache-aware HTTP/2 server push mechanism92. Keep in mind, though, that the new cache-digest specification93 will negate the need to manually build these “cache-aware” servers.
Use tree-shaking and code-splitting to reduce payloads.
Code-splitting100 is another Webpack feature that splits your code base into “chunks” that are loaded on demand. Once you define split points in your code, Webpack takes care of the dependencies and outputted files. It basically enables you to keep the initial download small and to request code on demand, when requested by the application.
Note that Rollup101 shows significantly better results than Browserify exports. While we’re at it, you might want to check out Rollupify102, which converts ECMAScript 2015 modules into one big CommonJS module — because small modules can have a surprisingly high performance cost103 depending on your choice of bundler and module system.
Improve rendering performance.
Isolate expensive components with CSS containment104 — for example, to limit the scope of the browser’s styles, of layout and paint work for off-canvas navigation, or of third-party widgets. Make sure that there is no lag when scrolling the page or when an element is animated, and that you’re consistently hitting 60 frames per second. If that’s not possible, then at least making the frames per second consistent is preferable to a mixed range of 60 to 15. Use CSS’ will-change105 to inform the browser of which elements and properties will change.
Use skeleton screens, and lazy-load all expensive components, such as fonts, JavaScript, carousels, videos and iframes. Use resource hints110 to save time on dns-prefetch111 (which performs a DNS lookup in the background), preconnect112 (which asks the browser to start the connection handshake (DNS, TCP, TLS) in the background), prefetch113 (which asks the browser to request a resource), prerender114 (which asks the browser to render the specified page in the background) and preload115 (which prefetches resources without executing them, among other things). Note that in practice, depending on browser support, you’ll prefer preconnect to dns-prefetch, and you’ll be cautious with using prefetch and prerender — the latter should only be used if you are very confident about where the user will go next (for example, in a purchasing funnel).
With Google moving towards a more secure web116 and eventual treatment of all HTTP pages in Chrome as being “not secure,” you’ll need to decide on whether to keep betting on HTTP/1.1 or set up an HTTP/2 environment117. HTTP/2 is supported very well118; it isn’t going anywhere; and, in most cases, you’re better off with it. The investment will be quite significant, but you’ll need to move to HTTP/2 sooner or later. On top of that, you can get a major performance boost119 with service workers and server push (at least long term).
The downsides are that you’ll have to migrate to HTTPS, and depending on how large your HTTP/1.1 user base is (that is, users on legacy operating systems or with legacy browsers), you’ll have to send different builds, which would require you to adapt a different build process122. Beware: Setting up both migration and a new build process might be tricky and time-consuming. For the rest of this article, I’ll assume that you’re either switching to or have already switched to HTTP/2.
Properly deploy HTTP/2.
Again, serving assets over HTTP/2123 requires a major overhaul of how you’ve been serving assets so far. You’ll need to find a fine balance between packaging modules and loading many small modules in parallel.
On the one hand, you might want to avoid concatenating assets altogether, instead breaking down your entire interface into many small modules, compressing them as a part of the build process, referencing them via the “scout” approach124 and loading them in parallel. A change in one file won’t require the entire style sheet or JavaScript to be redownloaded.
On the other hand, packaging still matters125 because there are issues with sending many small JavaScript files to the browser. First, compression will suffer. The compression of a large package will benefit from dictionary reuse, whereas small separate packages will not. There’s standard work to address that, but it’s far out for now. Secondly, browsers have not yet been optimized for such workflows. For example, Chrome will trigger inter-process communications126 (IPCs) linear to the number of resources, so including hundreds of resources will have browser runtime costs.
Still, you can try to load CSS progressively129128. Obviously, by doing so, you are actively penalizing HTTP/1.1 users, so you might need to generate and serve different builds to different browsers as part of your deployment process, which is where things get slightly more complicated. You could get away with HTTP/2 connection coalescing130, which allows you to use domain sharding while benefiting from HTTP/2, but achieving this in practice is difficult.
What to do? If you’re running over HTTP/2, sending around 10 packages seems like a decent compromise (and isn’t too bad for legacy browsers). Experiment and measure to find the right balance for your website.
Make sure the security on your server is bulletproof.
Different servers and CDNs are probably going to support HTTP/2 differently. Use Is TLS Fast Yet?139137 to check your options, or quickly look up how your servers are performing and which features you can expect to be supported.
Is Brotli or Zopfli compression in use?
Last year, Google introduced140Brotli141, a new open-source lossless data format, which is now widely supported142 in Chrome, Firefox and Opera. In practice, Brotli appears to be more effective143 than Gzip and Deflate. It might be slow to compress, depending on the settings, and slower compression will ultimately lead to higher compression rates. Still, it decompresses fast. Because the algorithm comes from Google, it’s not surprising that browsers will accept it only if the user is visiting a website over HTTPS — and yes, there are technical reasons for that as well. The catch is that Brotli doesn’t come preinstalled on most servers today, and it’s not easy to set up without self-compiling NGINX or Ubuntu. However, you can enable Brotli even on CDNs that don’t support it144 yet (with a service worker).
Alternatively, you could look into using Zopfli’s compression algorithm145, which encodes data to Deflate, Gzip and Zlib formats. Any regular Gzip-compressed resource would benefit from Zopfli’s improved Deflate encoding, because the files will be 3 to 8% smaller than Zlib’s maximum compression. The catch is that files will take around 80 times longer to compress. That’s why it’s a good idea to use Zopfli on resources that don’t change much, files that are designed to be compressed once and downloaded many times.
Is OCSP stapling enabled?
By enabling OCSP stapling on your server146, you can speed up your TLS handshakes. The Online Certificate Status Protocol (OCSP) was created as an alternative to the Certificate Revocation List (CRL) protocol. Both protocols are used to check whether an SSL certificate has been revoked. However, the OCSP protocol does not require the browser to spend time downloading and then searching a list for certificate information, hence reducing the time required for a handshake.
Have you adopted IPv6 yet?
Because we’re running out of space with IPv4147 and major mobile networks are adopting IPv6 rapidly (the US has reached148 a 50% IPv6 adoption threshold), it’s a good idea to update your DNS to IPv6149 to stay bulletproof for the future. Just make sure that dual-stack support is provided across the network — it allows IPv6 and IPv4 to run simultaneously alongside each other. After all, IPv6 is not backwards-compatible. Also, studies show150 that IPv6 made those websites 10 to 15% faster due to neighbor discovery (NDP) and route optimization.
Is HPACK compression in use?
If you’re using HTTP/2, double-check that your servers implement HPACK compression151 for HTTP response headers to reduce unnecessary overhead. Because HTTP/2 servers are relatively new, they may not fully support the specification, with HPACK being an example. H2spec152 is a great (if very technically detailed) tool to check that. HPACK works153.
Are service workers used for caching and network fallbacks?
No performance optimization over a network can be faster than a locally stored cache on user’s machine. If your website is running over HTTPS, use the “Pragmatist’s Guide to Service Workers157” to cache static assets in a service worker cache and store offline fallbacks (or even offline pages) and retrieve them from the user’s machine, rather than going to the network. Also, check Jake’s Offline Cookbook158 and the free Udacity course “Offline Web Applications159.” Browser support? It’s getting there160, and the fallback is the network anyway.
If you’ve recently migrated from HTTP to HTTPS, make sure to monitor both active and passive mixed-content warnings, with a tool such as Report-URI.io161. You can also use Mixed Content Scan162 to scan your HTTPS-enabled website for mixed content.
Is your development workflow in DevTools optimized?
Pick a debugging tool and click on every single button. Make sure you understand how to analyze rendering performance and console output, and how to debug JavaScript and edit CSS styles. Umar Hansa recently prepared a (huge) slidedeck163 and talk164 covering dozens of obscure tips and techniques to be aware of when debugging and testing in DevTools.
Have you tested in proxy browsers and legacy browsers? Testing in Chrome and Firefox is not enough. Look into how your website works in proxy browsers and legacy browsers. UC Browser and Opera Mini, for instance, have a significant market share in Asia165 (up to 35% in Asia). Measure average Internet speed166 in your countries of interest to avoid big surprises down the road. Test with network throttling, and emulate a high-DPI device. BrowserStack167 is fantastic, but test on real devices as well.
Is continuous monitoring set up?
Having a private instance of WebPagetest1686 is always beneficial for quick and unlimited tests. Set up continuous monitoring of performance budgets with automatic alerts. Set your own user-timing marks to measure and monitor business-specific metrics. Look into using SpeedCurve169 to monitor changes in performance over time, and/or New Relic170 to get insights that WebPagetest cannot provide. Also, look into SpeedTracker171, Lighthouse1727 and Calibre173.
This list is quite comprehensive, and completing all of the optimizations might take quite a while. So, if you had just 1 hour to get significant improvements, what would you do? Let’s boil it all down to 10 low-hanging fruits. Obviously, before you start and once you finish, measure results, including start rendering time and SpeedIndex on a 3G and cable connection.
Your goal is a start rendering time under 1 second on cable and under 3 seconds on 3G, and a SpeedIndex value under 1000. Optimize for start rendering time and time-to-interactive.
Prepare critical CSS for your main templates, and include it in the <head> of the page. (Your budget is 14 KB).
Defer and lazy-load as many scripts as possible, both your own and third-party scripts — especially social media buttons, video players and expensive JavaScript.
Add resource hints to speed up delivery with faster dns-lookup, preconnect, prefetch, preload and prerender.
Subset web fonts and load them asynchronously (or just switch to system fonts instead).
Optimize images, and consider using WebP for critical pages (such as landing pages).
Check that HTTP cache headers and security headers are set properly.
Enable Brotli or Zopfli compression on the server. (If that’s not possible, don’t forget to enable Gzip compression.)
If HTTP/2 is available, enable HPACK compression and start monitoring mixed-content warnings. If you’re running over LTS, also enable OCSP stapling.
If possible, cache assets such as fonts, styles, JavaScript and images — actually, as much as possible! — in a service worker cache.
With this checklist in mind, you should be prepared for any kind of front-end performance project. Feel free to download the print-ready PDF of the checklist as well as an editable Apple Pages document to customize the checklist for your needs:
Some of the optimizations might be beyond the scope of your work or budget or might just be overkill given the legacy code you have to deal with. That’s fine! Use this checklist as a general (and hopefully comprehensive) guide, and create your own list of issues that apply to your context. But most importantly, test and measure your own projects to identify issues before optimizing. Happy performance results in 2017, everyone!
Huge thanks to Anselm Hannemann, Patrick Hamann, Addy Osmani, Andy Davies, Tim Kadlec, Yoav Weiss, Rey Bango, Matthias Ott, Mariana Peralta, Jacob Groß, Tim Swalling, Bob Visser, Kev Adamson and Rodney Rehm for reviewing this article, as well as our fantastic community, which has shared techniques and lessons learned from its work in performance optimization for everybody to use. You are truly smashing! (al)
Back in 2007, the world met the iPhone for the very first time. After Apple’s product debut, it took less than six months for work to begin on PhoneGap, which would become one of the first and most adopted frameworks for hybrid mobile app development — that is, for apps written simultaneously for multiple platforms using HTML, CSS and JavaScript, rather than coded in native languages.
When compared with the prospect of learning an entirely new language and development environment in order to program iOS (and soon Android) apps, the appeal of this type of development to the already huge population of web developers in the world was palpable.
As with many things, however, execution in the real world didn’t quite live up to the hype.
It quickly became apparent that giving apps created in hybrid frameworks a “native” feel wasn’t always easy. Because these apps were essentially just rendering a web app in a native shell, mobile Internet connections and device hardware speeds at the time caused performance in many hybrid apps to range from “This is loading a little slower than my other apps” to “Apple straight up rejected this from the App Store for not behaving as expected!”
In short: Hybrid hadn’t quite delivered, and many would-be hybrid developers bit the bullet and learned to work in native development platforms, or decided to delay their app development ambitions indefinitely. It was kind of a bummer.
As time went on, however, advancements in technology — namely, phone hardware — allowed for enough progress in the performance of hybrid apps to drive some bold new claims. A notable example is the report published by industry research giant Gartner predicting that, by 2016, 50% or more of apps deployed to the App Store and Google Play would be hybrid. The prediction was published way back in 2013, and the figure of 50% was picked up3 and plastered4 on virtually every5 website covering the mobile development industry.
Now, many native frameworks and development tools even boast robust showcase libraries highlighting hybrid apps that have successfully made their way to market. Probably among the most notable are Ionic’s6 and Appcelerator’s7.
Have hybrid apps hit parity with their native counterparts yet? There have been several indications that we’re at least moving in that direction. At any rate, being in the latter half of 2016 warrants a renewed discussion of the hybrid versus native debate — and what opportunities it might hold for current developers.
Native Apps Are Still Faster, But That Statement’s Weight Is More Limited Than Before. Link
There’s no beating around the bush: In our current development world, there are still situations in which native apps load and move about with more agility than their hybrid counterparts. That being said, the difference in experience is far less noticeable than even just a few years ago. Software development outfit Azoft wrote over a full year ago that, in its experience, hybrid apps were in many cases “just as good as native apps.” Additionally, the general consensus has gone from “Native is better” to “Native is better in certain cases.” Those certain cases tend to boil down to a few key factors now:
Graphical behavior
Apps that need to utilize advanced 3D graphics, particle effects and multilayered animations are still not well suited to hybrid. Additionally, due to the knowledge and work put into such graphics, often used in games, the extent to which hybrid can expedite development (one of its main selling points) is diminished. This is because, where programming in hybrid frameworks can help you accomplish page-building and other app development tasks with less code, creating games and animations in general still requires specific knowledge and intensive work to get right.
Hardware responsiveness
Apps that require very quick, responsive access to things like a device’s accelerometer or similar hardware components are often still better suited to native development as well. This is because the need to call on these components with JavaScript — as is the case with hybrid apps — represents an extra step the device has to execute. That being said, this reality is declining in severity and will only continue to do so as phone hardware becomes more powerful.
CPU requirements
CPU-intensive apps (such as those that intercept camera input in real time to apply live filters, or that quickly render video, or that process large amounts of data simultaneously, etc.) are the other category of native-suited apps, due to the same logic we touched on above. Again, this gap will likely narrow over time.
Hybrid Apps Still Have a Shorter Development Cycle and Time-To-Market. Link
Even in an extremely pro-native post on Y Media Labs10, the author concedes that clients are probably better off seeking hybrid development “if the desired time to market is less than six months.” His experience is far from exclusive: Most articles11 that explore this debate12 conclude that development cycles can be greatly reduced by opting for hybrid.
Despite a rapidly growing number of developers with native coding skill sets, traditional app development is still a slow and complex process in many cases, and the timeframe within which a company or individual wants to get their app rolling can sometimes rule out native on its own.
Native tools regularly improve to help developers work more efficiently, but they are often outpaced in time savings by their counterparts in the hybrid world. The developers behind Ionic, a barebones framework for developing hybrid apps with raw HTML, CSS and JavaScript, recently launched Ionic Creator13, a product with some drag-and-drop elements for prototyping; newcomer Aquro14 is making waves by combining visual workflows with web coding in its own way; and enterprise-focused companies such as Telerik15 have similar platforms as well.
It’s also worth noting that much of this discrepancy in development efficiency between native and hybrid can be attributed to multiplatform projects. Because Android and iOS (and, in some hybrid platforms, Windows and web apps) can be developed simultaneously, the work hours needed to be put into a multiplatform mobile app can be significantly reduced by going hybrid. Plus, every time a client needs to update or add features to an app, again, those changes only have to be written once to be deployed across all of their platforms. That’s generally a big advantage for both developers and their clients.
This Shorter Development Cycle Usually Means Lower Costs, as Well. Link
In a Comentum article16 by app developer Bernard Kohan specifically comparing native development with development of hybrid projects in PhoneGap, he concluded that, depending on the size of an app project, businesses could save between 32 and 36% on their bill by opting for hybrid. When app development projects in the business world almost always operate in the five to six digit range, that can mean a difference of a lot of money, and clients will start to take notice and more often request hybrid development if they feel it meets their needs.
The research for Kohan’s writeup was conducted in January of 2015, but the same trends have further developed since, and more recent investigations still find differences in the cost associated with the two development strategies.
What This Means For You, And How To Take Advantage Of It Link
The real takeaway from this shifting dynamic is a massive business opportunity for those who bother to take advantage of it.
Hybrid app development is likely to enter a golden era, when more enterprise clients and mid-sized businesses will want apps, but pricing can still be placed at a premium until the market is saturated.
Savvy developers can still charge large development fees to create apps for these clients with hybrid technologies, while undercutting native costs just enough to give these clients a deal they can feel good about. Plus, they can develop these apps at a quicker rate, which means a high hourly income and a client that’s going to sing your praises for delivering their company’s app in two and a half months, when native-based firms have projected four to five or more.
In a few years, however, the time savings of hybrid development will be better known, the expectations of buyers will be more closely aligned with the actual time involved in the creation of these apps, and the number of developers offering hybrid development will be higher, increasing the need to bid for jobs at competitive prices. Remember that this exact trend has played out in the world of website development over the past couple of decades.
Let’s say, today in 2016, ACME Thumbtacks wants to contract a developer for an internal Android and iOS app to link up with its inventory system and let workers submit new orders from within the app when stock is low.
They approach a native development house, which quotes them $80,000 and gives a projected delivery date six months away. Armed with your favorite hybrid framework and development environment, you are approached by the client for a second opinion, and you let them know that you can complete the job in just two to three months, for $50,000.
Huge project savings and half the lead time?! They’d be fools not to go with you, and you pocket one heck of a price for a couple of months’ work. Of course, these numbers will vary wildly from project to project, depending on the customer’s needs and ability to pay, but you get the idea.
A word of caution: It is still important to be clear about client expectations for their app and the feasibility of those expectations within a hybrid development framework. This ensures you’ll avoid embarrassing situations in which you might over-promise on functionality that should really still be executed natively!
This Same Scenario Might Look Different A Few Years From Now. Link
ACME Thumbtacks now knows that their app’s requirements aren’t intensive enough to necessitate native development, so they explicitly look for hybrid developers. Because so many others have jumped on the trend and are perfectly happy with quoting a client just $20,000 for a few months of work, gone are the days of easy money!
While you’ll still have plenty of work as an app developer, your golden goose will have flown away, or at least will have become more of a, uh, bronze pigeon. Plus, as the hybrid market becomes more and more crowded, those early adopters with more satisfied clients, testimonials and connections will be in a good place to maintain a high level of demand.
Much like web development in the late 1990s and early 2000s, hybrid app development will be a skill set that can be sold at a premium over the next few years. That, my friends, is the definition of an opportunity!
Of course, a lingering stigma continues to dog the hybrid development world. Depending on the structure you’re working in (freelance or independent, or working in a firm with immediate superiors to report to, etc.), you might need to help more people overcome the perception that clients of hybrid development projects might be left with a subpar product.
One of the best things you can do to address this is to have those skeptics download a few of the apps from the showcase pages linked to earlier (or a few of PhoneGap’s17) and consider whether the experiences they encounter would be satisfactory to a client. The truth of the matter is that most of these apps are likely indistinguishable from natively developed ones.
In the end, the decision of whether to jump through the hoops necessary to switch gears and/or start a whole new hybrid venture is up to you. But, hey, those hoops might just end up being made of gold.
Hybrid development maintains its speed advantage over native coding, especially for apps that need to run on multiple platforms.
Phone performance has helped hybrid development grow out of its stigma of clunkiness, but it’s still far from a perfect solution for some project types.
The hybrid route currently presents a great opportunity for web developers to make a seamless (and lucrative) move into app development.
App development, whether native or hybrid, has an upward trajectory in demand that’s probably worthy of your attention over the coming years.
Apple taught us, “There’s an app for that.” And we believed it. Why wouldn’t we? But time has passed since 2009. Our mobile users have gotten more mature and are starting to weigh having space for new photos against installing your big fat e-commerce app. Meanwhile, mobile browsers have also improved. New APIs are being supported, and they will bring native app-like functionality to the mobile browser.
We can now access video and audio and use WebRTC to build a live video-chat web apps directly in the browser, no native app or plugin required. We can build progressive web apps that bring users an almost native app experience, with a launch icon, notifications, offline support and more. Using geolocation, battery status, ambient light detection, Bluetooth and the physical web, we can even go beyond responsive web design and build websites that will automagically adapt to users’ needs and context.
To help us dig into this world of new (and not so new) functionality and to guide us through this journey in the world of “Everything is possible in a mobile browser,” I will illustrate this article with some fictional but plausible use cases. Dear reader, meet Zoe, my user. She’s around 30 and works as a developer in our industry in the very near future. She works all day long on a computer, so she doesn’t want to have one at home, and she uses her smartphone as a primary device to navigate the web.
Part 1: Accessing And Handling Images, Video And Audio Directly In The Browser Link
Video and Audio Conference in the Browser (With WebRTC) Link
Zoe is invited to speak at a conference. Instead of adding her on Skype, like people usually do, the conference organizers send Zoe a link to an online video and audio conference web app, AppRTC1.
Zoe simply enters the correct room number. The browser asks her permission to access her camera and microphone, and that’s it: She gets connected with the other person. Zoe doesn’t need to install or update any additional plugin or app. Everything happens directly in the browser, no extra steps, no friction.
With a native app (especially on Android), you can ask users for a lot of permissions up front when they download your app. In the browser, users have to grant you access one API (i.e. one piece of functionality) at a time.
Using WebRTC4, you can open a direct real-time communication channel between two clients. You can then share sound, video and any other data directly between them.
This is powerful, but it’s supported only7 in Firefox and Chrome, and it’s under development for Edge and Safari. You will also need to access the video and audio streams. This can be done using the getUserMedia and Media Stream API8 (which is not supported9 in Internet Explorer or Safari).
Using this technology, we could imagine recreating a Google Hangouts or Facebook Messenger web app directly in the browser. The only thing missing would be access to the phone’s contacts. This is not currently possible from the browser with any kind of API.
Uploading a Picture From the Camera to the Browser Link
Zoe is asked by the conference’s organizers to fill her online bio for the conference. She logs into the website, goes to her account and finds the place to do so. When she taps on the “Update my picture” button, she can choose between taking a new picture with her camera or selecting a picture already taken. She chooses the first option and fills in her profile.
You can pass a comma-separated list of types of content files. On mobile, this would trigger a dialog in which users can chose between different content sources or direct access to the camera:
<input type="file" name="image" accept="image/*">
If you want the user to skip the selection dialog and directly access their camera to take a picture, you can add the capture attribute:
Now that we can directly access media (videos, images and audio) in the browser, a whole new world of possibilities has opened up. You could change your avatar right on the responsive website of any of your social media accounts, and you could take and upload photos and videos to your timeline instantly. If you want to sell your car or bike on Craigslist, you don’t need to take pictures of it and then upload them to your computer; you can create your ad and add the images right in your mobile browser.
If we want to go one step further, we could imagine recreating an Instagram-type app directly in the browser using CSS3 filters and input type files.
There’s also a really fun guitar tuner17 web app that uses your microphone to make sure your guitar (or voice) is perfectly tuned. Pretty handy, isn’t it? Again, you don’t need to install anything.
Part 2: Enhancing A Conference Website Into A Web App Link
In this second part, I want to show you how we can enhance the user experience on a conference website. You might be familiar with the concept of progressive enhancement on the web. I encourage you to apply it to the techniques that follow: Make sure that all of the main functionality on your website is accessible and works on a wide range of mobile browsers, and then progressively enhance the website with launch icons, notifications and offline support to build a better experience on mobile devices that support it.
Installing And Launching The Website As A Progressive Web App Link
To access a website, most users either will already have it bookmarked and hidden in a sea of bookmark folders or will simply look for it on their favorite search engine. One of the main arguments in favor of apps is the launch icon: It’s already there, ready on the user’s home screen.
Now, imagine that you could do the same for a website and launch it from the home screen, like you would do for any native app. Most modern browsers have this option available in a menu.
You’ll need some extra files and size to satisfy the range of browsers out there on the market, but this will work on iOS, Android (Chrome, Opera and Firefox) and Edge for mobile.
Not a lot of users know that they can add a website directly to their home screen. To make this functionality more discoverable, Chrome 42+ introduced the App Install Banner24. For the moment, the banner appears when users have visited a website at least twice, with at least five minutes having elapsed between visits.
Your website also has to meet a few technical criteria to trigger this banner, including having a service worker, being served over HTTPS (which we’ll get to later) and having a valid web app manifest file.
Specified by the W3C, a web app manifest27 is a simple JSON file that enables a developer to control a lot of things about their web app, including its name, icon, launch screen and theme colors, as well as how they want it to launch. Web Manifest Validator28 is a little online tool that will help you to validate the file. Once it’s created, you will need to link it to your website29.
{ "short_name" : "SuperConf", "name": "SuperConf, an amazing conference", // Defines a default URL to launch "start_url": "/index.html", "icons": [ { "src": "launchicon.png", "sizes": "96x96", "type": "image/png" } // Other icons go here ], // Launches the website without a URL bar "display": "standalone", // Provides a site-wide theme color "theme_color": "#fa9c00", // Provides a background color for the launch screen "background_color":"#ffffff" }
Starting in Chrome 47+, browsers use the manifest’s theme_color, background_color, name and icons to automatically generate a launch screen32 while a website loads.
With the display property35 in the manifest file, a developer can choose how they want the website to launch once it has been added to the home screen:
"display": "standalone" launches the website in full-screen mode, without any URL bar (currently supported in Chrome and Opera).
"display": "browser" launches the website in the conventional mode, with the URL bar visible.
The W3C also specifies fullscreen mode, which launches a website using the entirety of the available display area on a mobile device, and minimal-ui mode, which gives the user access to a minimal set of UI elements. Neither seems to be supported anywhere yet.
Developers can also force an orientation with "orientation": "landscape" or "orientation": "portrait", but unless you are building a video game, you might not want to use it.
Be Careful With meta name="apple-mobile-web-app-capable" on iOS Link
Display mode isn’t supported on iOS. The <meta name="apple-mobile-web-app-capable"> tag38 might look like it does the same thing, but it doesn’t. It will work on a web app with no links (AJAX-loaded content, for instance), but as soon as you use this on a traditional website, things get ugly. When the user launches the website from the launch icon, it will open full screen, but as soon as they tap on a link, it will open in a new tab in Safari. To prevent this, you will need some JavaScript to override the click event (which does not sound ideal).
Another fun thing you can do to delight users is to change the color of the URL bar. To change it for every page, you can use the HTML meta tag:
<meta name="theme-color" content="#db5945">
Here in France, we have a nice website that sells socks… only socks — but it does it quite well. Part of its success is due to the wide range of sock colors. And the color of the URL bar on every page matches the color of the displayed socks. This is one of those little details that can delight users and contribute to a fun overall experience.
Using the manifest.json file, you can also provide a site-wide theme color: "theme_color": "#133742".
Users will see this color in the URL bar as well as in the Android bar at the top when the website is in browser mode. It will also be used for the top bar of the splash screen, as seen before, and when the tab is displayed in a stack of many other tabs in multitasking mode.
If you want to provide a nice experience, there’s a lot to do and to think about, including making a lot of different sizes of icons for different operating systems. Some nice person has built a cool little tool named RealFaviconGenerator44. Feed it your favicon, and you’ll get a nice interface to play with and to tweak all of the things just mentioned. Grab the ZIP file, and voilà!
Zoe checks the program before the conference. From the little icon next to each talk, she understands that she can add a talk that she wants to attend to her schedule. A subscription button appears under each talk’s description, with short text explaining that users who have subscribed to the talk will get a notification 10 minutes before the talk starts, even if their browser is closed. Users must allow access through a browser dialog.
Being Smart When Asking Permissions on Mobile Browsers Link
The Chromium team put out an interesting document titled “Best Practices for Push Notifications Permissions UX47.” Users need to understand what they will gain from giving you access to their notifications. Explain why you need this access, and put it in context. I’m seeing more and more websites and blogs asking to send notifications the first time a user arrives on the home page. As a user, I might have arrived on a blog by following a link on Twitter and might not know what the blog is about, so I’m going to need more context before accepting notifications (if I ever do accept them). In the browser, when a user denies access, the website will not be able to ask for it again (unless the user goes into the website settings). So, be smart about when and where you ask. This is a general rule for all permissions, by the way (media, notifications, geolocation, etc.).
The conference day has finally arrived, and Zoe is getting a coffee when her phone vibrates. Even though the website and web app are closed, she has just gotten a notification directly on her locked phone, telling her that the first talk she has subscribed to will be starting in 10 minutes.
Mobile browser notifications can compete with native notifications because they get integrated directly in the notification center of the operating system. This means that they will get displayed outside of the browser or web app, even if it is closed. It will appear in the operating system’s notification center and will be displayed on the lock screen of the phone as well. Notifications in Chrome can also be shown in the desktop notification center of Windows 10 and Mac OS X.
A service worker is JavaScript that runs in the background once installed. It acts as a sort of small proxy, pushing notifications to the browser. The Push API is part of the family of service worker technologies. Once activated on the page, the service worker receives the push message, and then it is up to you how to notify the user (for example, using push messages or one-page notifications). A the time of writing, service workers are supported50 in Chrome, Firefox, Opera, Android browser and Chrome for Android (it is under consideration for WebKit), and the Push API is supported51 only in Firefox, Chrome and Chrome for Android 51.
In short, to make notifications work, you will need:
HTTPS on your server,
a declaration of a service worker,
the Push API,
the user’s acceptance.
That’s as far as my technical skill goes. If you want to go deeper, here are a few resources from people far more qualified on the topic than me:
Zoe might not have noticed, but the conference’s schedule was cached offline while she was browsing. This means that, even if she doesn’t always have an Internet connection during the conference, she can still visit the website.
This magic happens again with service workers. A service worker can intercept the user’s request and provide cached files to display the page faster, with or without a connection. The browser will first look for the cached files, instead of requesting the ones on the server. It can then also check whether the files need to be updated by looking for file modifications in the background. You can use this to make your website work offline, but you can also use it to cache part of the website — the user interface, if you like — to make it load faster.
I imagined all kinds of scenarios for Zoe when I was preparing for the ConFoo conference in March 2016. I wanted to create a demo page, but then in June I saw that Google I/O implemented everything I imagined, so I’ll let you play with that demo instead. Open your mobile browser, go to events.google.com/io201664, navigate to the schedule, and add some events to your list (you might need to log in with a Google account first). Then, add the website to your home screen, close Chrome, and launch the website from the home screen icon. Keep on adding things to your list. Switch to airplane mode, and you should see a short explanation that the connection has been lost and that changes will be synchronized with the server once you are back online. Add some more talks to the list, go back online, and voilà!
There are two other demos I really like:
Pokedex.org68 An online index of Pokemon characters (predating PokemonGo!).
204869 A fun game that has saved me from boredom during hours of air travel.
Again, open the website, load it to your home screen, switch to airplane mode, and then come back.
If you want to go deeper in the code, I would recommend the following reading:
I’ve described how to enhance a website to add some native-like functionality. You could go one step further with a full-on progressive web app. In their article “Instant Loading Web Apps With an Application Shell Architecture75,” Addy Osmani and Matt Gaunt refer to a progressive web app as a web app that “can progressively change with use and user consent to give the user a more native-app-like experience.” They cite an article in which Alex Russell76 describes progressive web apps as “websites that took all the rights vitamins.”
They work for every user, regardless of browser choice, because they’re built with progressive enhancement as a core tenet. This bring us back to what I said at the beginning of this section: Make sure your website works even if all of this fancy new technology is not supported, and treat all of this as progressive enhancement.
Responsive
They fit any form factor: desktop, mobile, tablet and whatever is next.
Connectivity-independent
They can be enhanced with service workers to work offline or on a slow network.
Fresh
They are always up to date, thanks to the service worker updating process.
Safe
They must be served via HTTPS to prevent snooping and to ensure that content hasn’t been tampered with.
Discoverable
They are identifiable as “applications” thanks to the manifest file and the service worker registration.scope, which allows search engines to find them (like any normal old-school website).
Re-engageable
They make re-engagement easy through features such as push notifications. Again, for me, this is not mandatory, and you might want to be careful with it.
Installable
They allow users to keep the apps they find most useful on their home screen, without the hassle of an app store.
Linkable
They can easily be shared via a URL and do not require complex installation.
App-like
They feel like an app to the user, with app-style interactions and navigation, because they’re built on the application shell model.
The last point about the application shell is where it gets really interesting and where progressive web apps bridge the gap between classic responsive websites and native apps. If you’ve created a native app, this should ring some bells. In a native app, the user would download the full UI (icons, fonts, etc.) when they install the app. Then, when they launch the app, the content is loaded from the server.
The concept of the application shell is pretty similar: It is the minimum HTML, CSS and JavaScript required to create your UI, the “chrome” of your interface. You would cache this to make the app load quickly. Then, the rest of the content would get dynamically loaded to populate the different views.
Also, the people at Opera have put together a selection of progressive web apps81. If you want a taste of what progressive web apps can do, you’ll find demos and inspiration there.
… comes great responsibility. There’s currently some82debate83 in the community about progressive web apps. Here are a few of the issues people are worrying about:
Is it really a good idea to hide URLs in a progressive web app? Developers have the choice, but it looked like Chrome was kind of in favor of standalone mode. And how are you supposed to share content? (Those awful share buttons might make a big comeback.)
A lot of current implementations seem to concentrate on the app part and forget the web part. Are we going to revert to dedicated mobile websites and desktop websites? I’m really looking forward to seeing more responsive and progressively enhanced demos.
Loading the application shell is like loading the chrome before the content, whereas many people want a “content-first” approach. Will users have to wait for your content to load even once the interface is already displayed?
If you ask me, not every website should be a progressive web app. For instance, transforming my portfolio84 into a progressive web app was pretty silly, but I did it for a demo; like many people in the industry, my portfolio is also a little playground. In truth, nobody (except maybe my mum) would install my portfolio as an app on their phone. And that’s totally fine, because let’s face it: My portfolio isn’t really app-ish.
So, I guess a progressive web app would be a great idea for websites that users visit on a regular basis, such as news websites and blogs (which is the criterion Chrome uses in determining whether to show the install banner), e-commerce and restaurant-delivery websites (from which users might order regularly), and anything task-oriented (i.e. app-ish). I would add social networks such as Facebook to the list, but building a progressive web app is far less interesting to those companies than building a native one — how are they supposed to collect and sell all of their users’ data if users can only access their services in the browser?
The thing about Facebook is that its mobile website works pretty well. But as soon as you try to view your messages, the website tries to open the Messenger app. Fun fact: Facebook has built a really nice responsive website for Messenger85 that works on desktop. You can shrink it to a mobile-ish size, and it still works. But as soon as you visit it on a mobile device (or with a mobile user agent), you get the mobile version of the website telling you that you need to install the app. So, when it comes to mobile apps versus progressive web apps (or responsive websites), even though we now have the tools and technology to build a lot of things in a mobile browser, there will always be different factors at play in the decisions of how to implement a service. The fact that you can’t access a phone’s address book from the browser is another piece of the puzzle.
To go deeper in this topic, you might want to read the following:
Part 3: Adapting The Website Or Web App To A User’s Current Needs And Context Link
Mobile devices are now equipped with a lot of different sensors that can get us a lot of information about our users (for best or worse). In this last part of the article, we will focus on how to enhance a website or web app for the user’s current needs, situation and context.
Back to Zoe for a moment. Thanks to the notification that popped up on her phone, she gets to the first talk in her schedule on time. She sits down in one of those comfortable theater chairs, and the room gets dark as the talk is about to start. She visits the conference website one last time before putting her phone away.
Using the light sensors on the device, we can adapt the luminosity or contrast of a website to the ambient light. Apart from making a website darker when the user is in a dark room, this could have a lot of practical applications. Many of the websites that my company builds get used in a private room (or on a couch), but that’s not the case for a lot of the professional products and interfaces I build. Those need to be used “in the field” — outside, inside, on rainy days, on sunny days, anything you can imagine.
For example, I was working on a crane-monitoring interface. The interface works in a Chrome browser on both desktop and tablet. The operator needs to see alerts when the wind is blowing too fast in order to change the cranes’ mode so that they don’t fall or collide with each other. The mobile device could be used in a really dark environment, or a lot of light might shine through the window directly onto the operator’s desk. Using ambient light sensors, we could maintain the contrast of the interface when there is too much light in the room, so that people monitoring the cranes will always be able to see alerts if something goes wrong.
In theory, there are two ways to do this. You could use the Ambient Light Sensor API94 to access the intensity level measured by the device’s light sensors. At the time of writing, this is supported95 only in Edge and Firefox for the desktop. A light-level query was expected in the “Media Queries Level 4” specification, but it seems to have been deferred to “Media Queries Level 596,” so this is not for us today.
Enhance Conference Feedback Using Bluetooth URL Transfer Link
When a talk ends, it’s usually feedback time. When I was at ConFoo, the conference organizers had participants fill out a short form at the end of each talk. Then, a team would collect the forms, read them, scan them and send them to me with an average grade. This was awesome; I got all of the feedback one hour after my talk. But I’m guessing it was also a lot of work for the staff. Let’s see how we can enhance this using Bluetooth, URLs and smartphones, shall we?
The Physical Web Applied to Conference Feedback Link
This is a good time to meet the “physical web.” Google has launched an initiative100 to “enable quick and seamless interactions with physical objects and locations.” The idea is to take advantage of the power of the web — hence, the URLs to share content and to allow users to interact with objects and locations in their surroundings without having to install anything. If you must, you could think of this as QR codes on steroids. Bluetooth beacons will broadcast URLs, and users’ phones in the vicinity will be able to catch those URLs and directly open websites or web applications.
Back to Zoe and our conference. A Bluetooth low-energy (BLE) beacon supporting the Eddystone protocol specification is embedded in the conference poster next to the door.
A little notification tells Zoe that a URL is being broadcast nearby, which her browser can scan and display.
Zoe opens the URL directly in her browser and fills in the feedback form online. Staff are no longer required to scan the forms, and the organizers can do this for every talk.
For this to work, users need to activate the physical web in the browser and also enable Bluetooth. The physical web is not activated by default (for now). It can be activated on Chrome for both Android and iOS107. The physical web has been supported in Chrome for iOS since July 2015, and is available on Chrome for Android in version 49+ and on devices running Kit Kat (4.4) and above.
The great thing about this is that you could integrate a beacon into almost anything: a street poster, a dog collar, a rental bike or a rental car. The idea is to use this for small interactions for which you would not really consider building a native app, but for which you could easily create a web page. In a video introduction to the physical web, Scott Jenson describes108 an interesting way to pay for a parking meter.
The physical web is about getting URLs to phones. What happens next is simply the web.
This is where things get exciting! In her talk “The Internet of Things Is for People109,” Stephanie Rieger explains how you can do a lot of useful stuff by combining a URL with a place (no need for geolocation — you’ve got a beacon here, remember) and a time (i.e. when the URL is triggered); for example, allowing the user to dig deeper into useful and relevant content. You have the exact context of the user; so, you can trigger a URL for content that adapts accordingly to the situation — an amazing, progressively enhanced experience tailored to the user’s needs. Bring service workers, WebRTC, notifications, progressive web apps and all of the technologies discussed earlier into the mix, and this, my dear designer and developer friends, is going to be powerful!
Another interesting example from Stephanie’s conference slides is Panic’s corporate sign in Portland110. The Panic team built an interactive sign for its building, and people can go to a website111 to change its color. How cool is that? The website is even a progressive web app. It’s fun to change the colors from where I live and imagine them changing on the building, but I’m a little far away. Many people walking around the area might not even be aware of this fun trick. With the physical web, we imagine that Panic could broadcast the URL of its fun little web app around the building, so that passersby can discover it.
If you are looking for more fun ideas for the physical web, check these out:
Not the physical web, but something similar: Disney’s Fun Wheel Challenge game is available via a URL over Wi-Fi, so that people can have fun while waiting in line for a ride. No app required!
The physical web is but one attempt to play with Bluetooth and browsers. There’s also the Web Bluetooth API116, which will allow web applications and websites to access services exposed by devices. This means that in the future, we will be able to directly connect and control objects (watches, sensors, smart thermostats, etc.) to a browser through Bluetooth. I say “in the future” because browser support is pretty low117 at the moment: Chrome, with a flag. Eventually, we will be able to do really fun things directly in the browser, like change the colors and the mood of this cute little turtle using only a website and Bluetooth connection:
Zoe arrives on Velibre’s website. A big button says “Find my location,” and a little snippet explains to Zoe that she will be able to find bike-rental stations nearby once she grants the website access to her location.
With the Geolocation API120, we can access the user’s current static location and also monitor changes in location when they move. Basically, you could do a lot of geolocation-related things that native apps do, directly in the browser. This is pretty well supported in every mobile browser121 (except for Opera mini). Remember that, in the browser, users have to grant access first. So, you might want to make clear why you need their location. Asking it when the user first visits the website is not the best idea. Again, always ask in context, and explain what the user will gain.
And don’t forget to progressively enhance. A lot can go wrong with geolocation: The user might not see the dialog box (which I’ve seen with my own eyes in user-testing sessions), GPS might not be available, or the user simply might not grant access. Always provide a fallback. It could be as simple as a free-text input field that the user can fill in with their current (or desired) location. Also, don’t assume that users always want a given service at their current location; let them change the location. There was a cinema app that relied only on the user’s current location to provide a schedule of movies. This is great if the user wants to see something nearby, but it doesn’t work so well if they are looking for something to watch on a trip out of town. Don’t assume the user’s intention, and use these technologies as enhancements, not defaults.
It was a really long day. Zoe used her phone a lot, and her battery is almost dead. She taps on Velibre’s button to ask for the closest bike station. The website detects that her battery is really low and loads a static map instead of an interactive one in order to save a bit of power.
The Battery Status API124 give you access to the battery level of the device. To be responsible, we could, say, propose fewer battery-consuming resources when the battery is low. This would be really useful for battery-draining functions such as GPS, peer-to-peer connections and animation. Codepen has a demo125 for you to play with. The API is currently supported126 in Chrome, Firefox and Opera for the desktop and in the latest version of Chrome for Android.
In this article, which is inspired by a talk I gave127, I wanted to present you with some APIs, some technologies and some of the cool things you can do with them to make your users’ lives easier. The future of the mobile browser is bright, shiny and fun. We can and will be able to build incredibly powerful things with web technologies.
In terms of mobile support for these technologies and APIs, it looks like the Android teams, followed by Firefox, Opera and Microsoft, are currently most focused on taking web apps to the next level and providing a more powerful experience for mobile browser users. iOS, on the other hand, is still far behind. Its lack of support for service workers might be the biggest issue here. If we truly want to be able to fill the gap between natives apps and mobile browsers, we need the notifications and offline functionality provided by service workers.
The big picture is complicated by more than just browser support. What would Apple gain by letting developers build web apps that don’t need to go in the App Store? Apple’s business model is tightly linked to iOS native applications? There’s an app for everything, right?
Is iOS holding us back? I don’t think so. We don’t need to embrace these new APIs and technologies as if they were supported everywhere. Building progressive web apps means building with performance in mind, while still providing a great mobile experience for users on all platforms and browsers. Make sure that users with devices that don’t support everything do not get frustrated with a blank page.
I’m just a designer, only one part of the whole chain. Now it’s your turn to build, to play, to share amazing websites and web apps!
Only one week left until Christmas, and people already start freaking out again. No gifts purchased yet, work isn’t finished either, and suddenly some budget has to be spent until the end of the year. All of this puts us under pressure. To avoid the stress, I’ve seen a lot of people take a vacation from now until the end of the year — probably a good idea.
And while it’s nice to see so many web advent calendars, I feel like I’ve never written a longer reading list than this one. So save this edition if you don’t have much time currently and read it during some calm moments later this year or early next year. Most articles are still worth reading in a few weeks.
Opera 42 (built upon Chromium 55) is out1 and comes with a built-in currency converter, support for Pointer Events, JavaScript async/await, and CSS hyphens. document.write() on the other hand, will no longer load2 over 2G connections.
Firefox has introduced Telemetry a while ago to its browser and now shares some details on what devices and hardware Firefox users use4. In September 2016, for example, 10% still used Windows XP while only 7% used macOS and 77% of the users still have Flash installed. The most common screen resolutions are 1366x768px and 1920x1080px. There are many more really interesting statistics in there, and we’ll have to see how this develops over the next few years. But for us web developers, this also highlights that we shouldn’t assume that people use QuadCore CPU, 8GB RAM machines but have “lower-end” devices instead. So be aware of this before you create fancy CPU/memory-consuming web applications that a user will not have fun with.
Samsung Internet browser 5.0 has been released5. It has some interesting new technologies built in, such as content provider extensions, 360˚ video, a QR code reader, and a video assistant.
A lot of us are using Disqus’ commenting system on their websites. It’s an easy way to add comments to your static website, but now Disqus announced that they need to lay off about 20% of their employees. But not only that, they will also change their strategy towards data collection and advertising10. Specifically, they elaborate on displaying ads in comments, and there are speculations that they will try to sell (anonymized) user data to advertisers to help them tailor their ads more precisely to users. Maybe time to reconsider if you really want to use the service.
Sergey Chikuyonok dives deep into the technical details of browsers and hardware to explain how to get GPU animation right13 and why it makes a big difference if we render animations on the CPU or on the GPU.
[...'???'] // ["?", "", "?", "", "?"] or ‘???’.length // 8 — do you wonder why that works? Stefan Judis found out and shares the technical details on why the Emoji family works so well with JavaScript operations21 and how you even can dynamically generate the skin color of an emoji22 with color codes and the unicode.
Working long hours doesn’t mean someone has a good “work ethic”. Instead, it just means working too much. Jason Fried on what “work ethic” really is about32.
The blank Photoshop document glows in front of you. You’ve been trying to design a website for an hour but it’s going nowhere. You feel defeated. You were in this same predicament last month when you couldn’t design a website for a project at work. As a developer, you just feel out of your element pushing pixels around.
How do designers do it? Do they just mess around in Photoshop or Sketch for a while until a pretty design appears? Can developers who are used to working within the logical constructs of Boolean logic and number theory master the seemingly arbitrary rules of design?
You can! You don’t have to be blessed by the design gods with special talent. So, how should you, a developer, learn design?
One of the quickest ways to learn something is to ask someone who has done it and pick their brain. I spoke with a handful of developers who did just that. Some learned design to supplement their coding skills, while others switched over completely.
This article is for design beginners. So, throughout the piece, I’ll use simplified definitions of user experience (UX) and visual design. For our purpose, UX design is how a website works, and visual design is how it looks. Both fields are, of course, more nuanced1 than that, but in the interest of time, those definitions should suffice.
Let’s get started with mistakes designers make when learning design!
An 18-year-old freshman has four years to discover what area of design they like. At design school, they’ll dabble in motion graphics for a while, then maybe try UX. At some point, they’ll be obsessed with photography for a semester. Eventually, the student will find an area of design that they enjoy and are good at.
But you’re a working developer with limited time. You need to be specific about what you want to learn.
For most developers, the first step is to pick either UX design or visual design (usually synonymous with graphic design). Many tutorials do not distinguish between the two, which can be frustrating.
So, should you try UX or visual design? Consider which one you’re more drawn to and then try it out. If browsing Dribbble excites you, then try visual design. If you’d rather get lost in one of Edward Tufte’s books, try UX. My guess is that one of the two fields has already whispered to you in the past. Listen to that whisper.
Or you may have already figured out your preference by working in the field. For Jenny Reeves5, a LAMP developer turned UX designer, the transition came naturally. She says:
It happened over time, so it wasn’t like I woke up one day and said I’m going to switch roles. I started doing IA diagrams and user flows when I was a developer as a means to get applications organized, then moved into making wireframes for basic stuff. After I realized my passion for this and my company took notice, I soon started doing all in-house UX.
Jacob Rogelberg6, former JavaScript developer turned UX specialist, thinks you need to try many things before choosing:
I think you have to have a mindset where you’re happy to try lots of different things. Commit to spending 10 hours on visual design and see how it sits with you.
During this trial phase, don’t mistake your inexperience with a lack of natural talent (more on that later).
Why am I recommending that you choose just one? Because when you remove visual or UX design from the equation, you’re left with 50% less material to digest. It’s true that UX and visual design overlap in places. And some people will argue that you should learn both. But that comes at the expense of focus and your limited time.
By picking just one, you’ll get better faster. Mastering either visual or UX design is much better than being mediocre at both.
Let’s say you’ve picked visual design:
That’s a broad field, so we need to get more specific. There’s no sense in learning topics you’re never going to use as a developer. For instance, you probably won’t need to learn all of the aspects of editorial design or book-cover design or logo design.
I recommend that 90% of developers focus on interactive work if they’re learning visual design. (If you’re learning UX design, most work in that field is on the web already, so this point isn’t as relevant.)
I know, I know, you want to try everything. But learning something such as logo design isn’t worth your time. In the span of your lifetime, there will be very few instances when you, the web developer, will be asked to design a logo for a company. However, focusing only on interactive visual design (websites and apps) would benefit you for years to come.
As a web developer, you’re going to be drawn to subjects that seem design-related — for example, CSS, Bootstrap and material design. But learning this stuff will come at the expense of what you should really be focusing on. For visual design, those things are composition, hierarchy and typography. For UX, they’re user research and personas.
Developers are builders. While learning design, you’re going to have an urge to build. Be mindful of when it happens, and tell yourself you can code later.
David Klawitter13 is a design lead at Detroit Labs14, where he previously worked as a developer. Instead of giving in to his urge to program while at work, he hacks away on personal projects at home. He states:
I think that there’s this natural tendency to want to build the thing that’s in your mind — that’s one of the most exciting aspects about being a developer. I found it very difficult to peel myself away from that desire. I still get to scratch that itch on personal (technical) projects, but the scope of our work at Detroit Labs and my responsibilities just don’t allow that. I provide more value by having a broader and deeper understanding of design, and that means focusing in on it.
If you don’t already know the technical side of front-end design work, it will come easily once you decide to learn it. Therefore, work on the areas of design you’re unfamiliar with. That’s where there’s more opportunity to grow.
I know a lot of web developers who can use Photoshop and Illustrator. They know the tools well, but they can’t utilize them to solve design problems. In the graphic design world, these people are known as production artists. It’s a fine profession to be in, but it’s different from being a designer.
It’s tricky. You need to know the tools to be a designer, but knowing them won’t make you a designer.
I fell into this trap at design school. My first year was dedicated to learning Photoshop, Illustrator and InDesign. It felt good to master those tools. At the end of the year, I thought, “I’ve made it. Now I’m a designer!” But I soon realized that learning the tools was just step one. Step two was about making the work look good. My peers in design school who understood that kept pushing themselves and progressed from being production artists into full-fledged designers.
Jacob Rogelberg again:
My mentors always told me, “Don’t get stuck on the tools.” It’s concepts first, tools last. It takes a while for that to become ingrained. You need to understand problems. It’s like how thinking about a coding challenge is more important than knowing the specific programming language it’s written in.
Sean Duran15 is a filmmaker who used to be a designer and developer. He says:
From the beginning, I assumed that if I learned the tools, I could be a designer. But they’re definitely two separate things. And it was a struggle sometimes. You could learn every command and tool in Photoshop and still not be a great designer. What helped me the most was looking at others’ work and thinking about why it was good or bad.
Learn the tools first, and pat yourself on the back for learning them. But then dig deep and hone your design skills.
Concentrating On Visual Effects, Rather Than Design Link
This is related to what we just discussed and only applies to visual design, not UX design. Many tutorials, like this one16, will make you feel like you’re learning design. They’re a great way to learn Photoshop, but they can be a distraction.
Most of these tutorials are in some way related to style, one of many principles under the umbrella of design:
If you only work on style-related tutorials, you’ll never be good at the other principles of visual organization, and then you’ll wonder why your designs look bad. It’s tough because the majority of design tutorials on the web fall into this category of style and technique. That’s because things such as hierarchy, concept and composition are more abstract and don’t make for very compelling or easy-to-understand tutorials.
Instead of repeatedly working in the style category, use the diagram above to research and practice the other design principles.
By the way, none of the professional designers I know pay much attention to Photoshop tutorials or websites like the ones cited above. The only time they might seek them out is if they’re trying to achieve a particular look for a project.
How much time should you spend reading design books to understand the basics? I think being able to make a design decision and then evaluating it based on specific design principles will indicate that you have enough design theory under your belt to move on. For example, you should be able to say to yourself things like, “Those two sections on the home page are too close in size — the hierarchy is off,” or “These three lines of type don’t read well because they aren’t aligned.”
Book knowledge is certainly important. But your skills will improve the most when you’re in practice mode, not research mode. In fact, design theory makes sense more quickly once you start to use it in practice.
Greg Koberger20, a designer, developer and startup founder, is biased towards action over theory:
I always advocate doing rather than reading. All the good developers I know learned by just making something they wanted to make, and figured out the tech. The tech was a means to an end. I’m an advocate of trying and playing around on your own, and then using articles to help with your form once you’ve gotten your feet sufficiently went. The same is true for design.
Learn design theory, but don’t bury yourself in books. Practice!
I’ve seen developers look at work on Dribbble and get so intimidated that they never get started. That’s a trap.
Odds are, if you’re reading this, you want to learn either UX or visual design to supplement your coding skills. You’re a coder first, so at the office your design skills shouldn’t be compared to those of the stars on Dribbble, just as my coding skills as a visual designer wouldn’t measure up to a front-end developer’s.
People get hired based on their strengths. And the Dribbble folks have been perfecting their craft for years, while you’ve been doing it in your spare time. So, of course, your work won’t be as tight.
Smart recruiters and companies understand this. Savoy Hallinan21 is a savvy creative recruiter from Los Angeles:
If I’m asked to find a developer who can also design, I’ll ask “What level of design is needed?” If it’s a significant amount, I’ll recommend two people, because I don’t like to set people up for failure. And if I’m interviewing a developer/designer, I always ask, what’s your primary function? Because people tend to be stronger in one area or the other.
Take a few steps back and understand why you’re really doing this. Are you learning visual design because you’ve always been a creative person and you need an outlet? If so, that’s fine. Do it for the joy, and don’t worry about trying to market yourself that way. Maybe it monetizes later, maybe not. Be OK with that. Lots of people are bored by their paid work but find other outlets to express themselves, and are happy as a result.
Are you learning visual design because you’ve been rejected by employers for your lack of visual design? Are you sure that’s why they rejected you? Sometimes employers don’t give you the real reason, or any reason, for not hiring you.
In a market this tight for talented engineers, it would be very surprising if you passed tech screenings and were a strong culture fit, but the only reason they didn’t hire you was because of a lack of visual skills.
Don’t get so intimidated by great work that you never get started. You’re a developer first, and that’s OK!
Some developers think the ability to design is either in you or it isn’t. I think there’s some truth to that. Many of the best designers in the world were artistically inclined when they were kids. Just as you were compelled to take apart answering machines and blenders, they were busy drawing Rainbow Bright and Ninja Turtles.
Natural talent is certainly a factor in how good you can be at design. However, as we discussed earlier, you aren’t competing with those top designers on Dribbble. Even if you don’t have the natural talent, you can still get very good at design with dedication and practice.
Greg Koberger again:
Design is a hard skill. Everyone has the physical body parts to do it, but not everyone has the desire to practice and work at it. So, it’s less about “Do I have this skill” and more about “How badly do I want it?”
Practicing anything is hard, in particular when it’s outside of your comfort zone. But keep at it and give yourself credit for your small victories along the way.
When it comes to practicing, give yourself a trial run of 20 hours to learn design. That’s a reasonable amount of time to commit. If you don’t see your skills improve in that amount of time or you hate it, then at least you can say you tried your best and didn’t make excuses.
Do you work with designers? Not many people have that resource. Ask them how you can better understand the design files they’re providing you to mark up. Or ask if you can help them with some grunt work and get their feedback afterwards.
Most people like talking about subjects they’re good at. And if you defer to them in a way that makes them feel like a teacher, they’ll behave like a teacher and want to help you out.
Mikey Micheletti24, a creative polymath from Seattle, learned UX at work. He says:
In the mid-90s, I was doing development work on internal-facing desktop applications, laying out controls on the screen, and not doing a very good job of it. I spoke with another developer who was really good at it, and he taught me the basics of layout, alignment, flow. Even with just a smidgen of knowledge, my work improved and people liked it better.
If you’re around designers during the day, use it to your advantage.
This route can be challenging because you have to simultaneously be the teacher and the student. And when you bounce around from tutorial to tutorial, frustration sets in easily. If you were to go this route, I would suggest two steps:
Figure out which particular area of design you want to learn and find the best books on the subject. For visual design, try books by Steven Heller, Ellen Lupton and Philip Meggs. For UX, look at Don Norman and Edward Tufte.
Create assignments for yourself and then get feedback from a professional designer. Having that feedback will accelerate your skills faster than working in isolation.
Greg Koberger thinks copying other people helps you learn:
I learned by basically creating collages of things I liked. A button design from one site, the nav from another. Once I had a frankensite, I would redo it until two things happened:
I learned the tools.
I had made it my own. This really helped me learn the tools. (Copying is a great way to learn, much like how art students repaint classic paintings at museums to learn). So, when I became more creative, I could rapidly test out new things
Many developers don’t want to go this route because of the time commitment. But if you have the time and money, I would recommend this option. Being in a classroom and having a dialogue with a teacher is a higher-bandwidth way of absorbing the material. I know that massive open online courses (MOOCs) are all the rage, but don’t discount the value of physically being in a classroom.
Michael Loomes25, an iOS developer turned visual designer, went this route:
I studied for a Bachelor of Communication Design at Billy Blue College of Design in Sydney for two years. I feel this was a really good decision as it taught me a lot about “design thinking” as well improving the technical side of things. I think this “design thinking” side of things is something that isn’t that easy to teach yourself and would come after years of experience. You can only learn so much by following online tutorials.
One more thing about school: If you’re considering taking night classes, make sure they’re legit. Good design courses are usually taught at a university rather than a community college, where classes are more tool-focused.
I hope this advice is helpful! If you’re interested in visual design and you want to get a good grasp on the basics, I would start with these books: Thinking With Type26, Layout Essentials27, and Meggs’ History of Graphic Design28. I personally don’t recommend “web design” books because they’re heavy on tech and light on design theory. I also have a free course29 that uses specific examples to teach visual web design in a practical way. Lastly, if you want to learn UX design, I recommend checking out 52 Weeks of UX30. It’s a great resource to start with.
With the holidays almost here and the new year already in sight, December is a time to slow down, an occasion to reflect and plan ahead. To help us escape the everyday hectic for a bit and sweeten our days with a delightful little surprise each day up to Christmas, the web community has assembled some fantastic advent calendars this year. They cater for a daily dose of web design and development goodness with stellar articles, inspiring experiments, and even puzzles to solve.
To make the choice of which ones to follow a bit easier, we collected a selection of advent calendars in this Quick Tip for you. No matter if you’re a front-end dev, UX designer, or content strategist, we’re certain you’ll find something to inspire you for the upcoming year. So prepare yourself a nice cup of coffee, cozy up in your favorite chair and, well, enjoy!
Already in its 12th year, 24 Ways1 is a real classic under the advent calendars for the web community. Each day up to Christmas, it caters for a daily dose of web design and development goodness by some of the most renown minds in the web industry.
To cater for some magic moments this holiday season, Christmas Experiments3 delivers a new jaw-dropping digital experiment by top web creatives as well as promising newcomers each day.
You probably know the “12 Days Of Christmas” song. But do you also know 12 Devs Of Christmas5? If not, make sure to check it out — there’s a new article with exciting new thoughts and ideas from the web dev world waiting each day.
To make the holiday season a favorite time for speed geeks, the Performance Calendar7 shares a piece of advice each day up to Christmas that is bound to improve the performance of your website.
“Giving back little gifts of code for Christmas.” That’s the credo of 24 Pull Requests9. The idea is to get involved and contribute to the open-source projects we have benefited from this year — by improving documentation, fixing issues, improving code quality, etc.
What are other web designers and developers excited about these days? The collection of writings over on the Web Advent Calendar15 sheds some light into the dark.
Here’s one for the content strategists amongst you: The 2016 Content Strategy Advent Calendar17 by GatherContent publishes a video a day until Christmas, in which content strategy experts share their advice, talk about their hot content topics and reveal their focus for 2017.
The PHP family shares their thoughts in a daily article over on 24 Days In December21. The article covers a wide range of topics related to PHP: from security to performance to techniques to workflow to tooling!
If you’re looking for something to put your skills to the test, then Advent Of Code23 is for you. Each day up to the 25th, it holds a new small programming puzzle for you to master.
The Perl 6 Advent Calendar25 shares something cool about Perl 6 every day. And if that’s not enough Perl wisdom for you yet, also check out Perl Advent26.
The advent calendar of the open-source machine emulator and virtualizer Qemu28 hosts a collection of Qemu disk images — from operating systems (old and new) to custom demos and neat algorithms — that you can run in the emulator.
AWS Advent30 explores all things related to Amazon Web Services. You’ll find a wide range of article about security, deployment strategy and general tips and techniques to be aware of when using Amazon Web Services.
Last but not least, sys admins get their daily goodie, too, this year — on Sysadvent32. In 25 articles, fellows sys admins share strategies, tips, and tricks about system administration topics.
Do you follow along an advent calendar this year? Maybe it’s in French or Russian, rather than English? Let us know about your favorites in the comments below!
Web components1 are an amazing new feature of the web, allowing developers to define their own custom HTML elements. When combined with a style guide, web components can create a component API2, which allows developers to stop copying and pasting code snippets and instead just use a DOM element. By using the shadow DOM, we can encapsulate the web component and not have to worry about specificity wars3 with any other style sheet on the page.
However, web components and style guides currently seem to be at odds with each other. On the one hand, style guides provide a set of rules and styles that are globally applied to the page and ensure consistency across the website. On the other hand, web components with the shadow DOM prevent any global styles from penetrating their encapsulation, thus preventing the style guide from affecting them.
So, how can the two co-exist, with global style guides continuing to provide consistency and styles, even to web components with the shadow DOM? Thankfully, there are solutions that work today, and more solutions to come, that enable global style guides to provide styling to web components. (For the remainder of this article, I will use the term “web components” to refer to custom elements with the shadow DOM.)
What Should A Global Style Guide Style In A Web Component? Link
Before discussing how to get a global style guide to style a web component, we should discuss what it should and should not try to style.
First of all, current best practices for web components4 state that a web component, including its styles, should be encapsulated, so that it does not depend on any external resources to function. This allows it to be used anywhere on or off the website, even when the style guide is not available.
Below is a simple log-in form web component that encapsulates all of its styles.
Note: Code examples are written in the version 1 specification for web components.
However, fully encapsulating every web component would inevitably lead to a lot of duplicate CSS, especially when it comes to setting up the typography and styling of native elements. If a developer wants to use a paragraph, an anchor tag or an input field in their web component, it should be styled like the rest of the website.
If we fully encapsulate all of the styles that the web component needs, then the CSS for styling paragraphs, anchor tags, input fields and so on would be duplicated across all web components that use them. This would not only increase maintenance costs, but also lead to much larger download sizes for users.
Instead of encapsulating all of the styles, web components should only encapsulate their unique styles and then depend on a set of shared styles to handle styles for everything else. These shared styles would essentially become a kind of Normalize.css6, which web components could use to ensure that native elements are styled according to the style guide.
In the previous example, the log-in form web component would declare the styles for only its two unique classes: .container and .footnote. The rest of the styles would belong in the shared style sheet and would style the paragraphs, anchor tags, input fields and so on.
In short, the style guide should not try to style the web component, but instead should provide a set of shared styles that web components can use to achieve a consistent look.
How Styling The Shadow DOM With External Style Sheets Used To Be Done Link
The initial specification for web components (known as version 0) allowed any external style sheet to penetrate the shadow DOM through use of the ::shadow or /deep/ CSS selectors. The use of ::shadow and /deep/ enabled you to have a style guide penetrate the shadow DOM and set up the shared styles, whether the web component wanted you to or not.
/* Style all p tags inside a web components shadow DOM */ login-form::shadow p { color: red; }
With the advent of the newest version of the web components specification (known as version 1), the authors have removed the capability7 of external style sheets to penetrate the shadow DOM, and they have provided no alternative. Instead, the philosophy has changed from using dragons to style web components8 to instead using bridges. In other words, web component authors should be in charge of what external style rules are allowed to style their component, rather than being forced to allow them.
Unfortunately, that philosophy hasn’t really caught up with the web just yet, which leaves us in a bit of a pickle. Luckily, a few solutions available today, and some coming in the not-so-distant future, will allow a shared style sheet to style a web component.
The only native way today to bring a style sheet into a web component is to use @import. Although this works, it’s an anti-pattern9. For web components, however, it’s an even bigger performance problem.
Normally, @import is an anti-pattern because it downloads all style sheets in series, instead of in parallel, especially if they are nested. In our situation, downloading a single style sheet in series can’t be helped, so in theory it should be fine. But when I tested10 this in Chrome, the results showed that using @import caused the page to render up to a half second slower11 than when just embedding the styles directly12 into the web component.
Note: Due to differences in how the polyfill of HTML imports13 works compared to native HTML imports, WebPagetest.org14 can only be used to give reliable results in browsers that natively support HTML imports (i.e. Chrome).
In the end, @import is still an anti-pattern and can be a performance problem in web components. So, it’s not a great solution.
Because the problem with trying to provide shared styles to web components stems from using the shadow DOM, one way to avoid the problem entirely is to not use the shadow DOM.
By not using the shadow DOM, you will be creating custom elements16 instead of web components (see the aside below), the only difference being the lack of the shadow DOM and scoping. Your element will be subject to the styles of the page, but we already have to deal with that today, so it’s nothing that we don’t already know how to handle. Custom elements are fully supported by the webcomponentjs polyfill, which has great browser support17.
The greatest benefit of custom elements is that you can create a pattern library18 using them today, and you don’t have to wait until the problem of shared styling is solved. And because the only difference between web components and custom elements is the shadow DOM, you can always enable the shadow DOM in your custom elements once a solution for shared styling is available.
If you do decide to create custom elements, be aware of a few differences between custom elements and web components.
First, because styles for the custom element are subject to the page styles and vice versa, you will want to ensure that your selectors don’t cause any conflicts. If your pages already use a style guide, then leave the styles for the custom element in the style guide, and have the element output the expected DOM and class structure.
By leaving the styles in the style guide, you will create a smooth migration path for your developers, because they can continue to use the style guide as before, but then slowly migrate to using the new custom element when they are able to. Once everyone is using the custom element, you can move the styles to reside inside the element in order to keep them together and to allow for easier refactoring to web components later.
Secondly, be sure to encapsulate any JavaScript code inside an immediately invoked function expression (IFFE), so that you don’t bleed any variables to the global scope. In addition to not providing CSS scoping, custom elements do not provide JavaScript scoping.
Thirdly, you’ll need to use the connectedCallback function of the custom element to add the template DOM to the element19. According to the web component specification, custom elements should not add children during the constructor function, so you’ll need to defer adding the DOM to the connectedCallback function.
Lastly, the <slot> element does not work outside of the shadow DOM. This means that you’ll have to use a different method to provide a way for developers to insert their content into your custom element. Usually, this entails just manipulating the DOM yourself to insert their content where you want it.
However, because there is no separation between the shadow DOM and the light DOM with custom elements, you’ll also have to be very careful not to style the inserted DOM, due to your elements’ cascading styles.
<!-- login-form.html --> <template> <style> login-form .container { max-width: 300px; padding: 50px; border: 1px solid grey; } login-form .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template> <script> (function() { const doc = (document._currentScript || document.currentScript).ownerDocument; const template = doc.querySelector('#login-form-template'); customElements.define('login-form', class extends HTMLElement { constructor() { super(); } // Without the shadow DOM, we have to manipulate the custom element // after it has been inserted in the DOM. connectedCallback() { const temp = document.importNode(template.content, true); this.appendChild(temp); } }); })(); </script>
Aside: A custom element is still a web component for all intents and purposes. The term “web components” is used to describe four separate technologies23: custom elements, template tags, HTML imports and the shadow DOM.
Unfortunately, the term has been used to describe anything that uses any combination of the four technologies. This has led to a lot of confusion around what people mean when they say “web component.” Just as Rob Dodson discovered24, I have found it helpful to use different terms when talking about custom elements with and without the shadow DOM.
Most of the developers I’ve talked to tend to associate the term “web component” with a custom element that uses the shadow DOM. So, for the purposes of this article, I have created an artificial distinction between a web component and a custom element.
Another solution you can use today is a web component library, such as Polymer25, SkateJS26 or X-Tag27. These libraries help fill in the holes of today’s support and can also simplify the code necessary to create a web component. They also usually provide added features that make writing web components easier.
For example, Polymer lets you create a simple web component in just a few lines of JavaScript. An added benefit is that Polymer provides a solution for using the shadow DOM and a shared style sheet28. This means you can create web components today that share styles.
To do this, create what they call a style module, which contains all of the shared styles. It can either be a <style> tag with the shared styles inlined or a <link rel="import"> tag that points to a shared style sheet. In either case, include the styles in your web component with a <style include> tag, and then Polymer will parse the styles and add them as an inline <style> tag to your web component.
<!-- shared-styles.html --> <dom-module> <!-- Link to a shared style sheet --> <!-- <link rel="import" href="styleguide.css"> --> <!-- Inline the shared styles --> <template> <style> :host { color: #333333; font: 16px Arial, sans-serif; } /* Rest of shared CSS */ </style> </template> </dom-module>
The only downside to using a library is that it can delay the rendering time of your web components. This shouldn’t come as a surprise because downloading the library’s code and processing it take time. Any web components on the page can’t begin rendering until the library is done processing.
Again, Polymer does nothing in particular to make the rendering time slower. Downloading the Polymer library and processing all of its awesome features, plus creating all of the template bindings, take time. It’s just the trade-off you’ll have to make to use a web component library.
If none of the current solutions work for you, don’t despair. If all goes well, within a few months to a few years, we’ll be able to use shared styles using a few different approaches.
To declare a custom property, use the custom property notation of --my-variable: value, and access the variable using property: var(--my-variable). A custom property cascades like any other CSS rule, so its value inherits from its parent and can be overridden. The only caveat to custom properties is that they must be declared inside a selector and cannot be declared on their own, unlike a preprocessor variable.
<style> /* Declare the custom property */ html { --main-bg-color: red; } /* Use the custom property */ input { background: var(--main-bg-color); } </style>
One thing that makes custom properties so powerful is their ability to pierce the shadow DOM. This isn’t the same idea as the /deep/ and ::shadow selectors because they don’t force their way into the web component. Instead, the author of the web component must use the custom property in their CSS in order for it to be applied. This means that a web component author can create a custom property API that consumers of the web component can use to apply their own styles.
<template> <style> /* Declare the custom property API */ :host { --main-bg-color: brown; } .one { color: var(--main-bg-color); } </style> <div>Hello World</div> </template> <script> /* Code to set up my-element web component */ </script> <my-element></my-element> <style> /* Override the custom variable with own value */ my-element { --main-bg-color: red; } </style>
Browser support for custom properties is surprisingly good36. The only reason it is not a solution you can use today is that there is no working polyfill37 without Custom Elements version 1. The team behind the webcomponentjs polyfill is currently working to add it38, but it is not yet released and in a built state, meaning that if you hash your assets for production, you can’t use it. From what I understand, it’s due for release sometime early next year.
Even so, custom properties are not a good method for sharing styles between web components. Because they can only be used to declare a single property value, the web component would still need to embed all of the styles of the style guide, albeit with their values substituted with variables.
Custom properties are more suited to theming options, rather than shared styles. Because of this, custom properties are not a viable solution to our problem.
In addition to custom properties, CSS is also getting @apply rules39. Apply rules are essentially mixins for the CSS world40. They are declared in a similar fashion to custom properties but can be used to declare groups of properties instead of just property values. Just like custom properties, their values can be inherited and overridden, and they must be declared inside a selector in order to work.
Browser support for @apply rules is basically non-existent. Chrome currently supports it41 behind a feature flag (which I couldn’t find), but that’s about it. There is also no working polyfill for the same reason as there is no polyfill for custom properties. The webcomponentjs polyfill team is also working to add @apply rules, along with custom properties, so both will be available once the new version is released.
Unlike custom properties, @apply rules are a much better solution for sharing styles. Because they can set up a group of property declarations, you can use them to set up the default styling for all native elements and then use them inside the web component. To do this, you would have to create an @apply rule for every native element.
However, to consume the styles, you would have to apply them manually to each native element, which would still duplicate the style declaration in every web component. While that’s better than embedding all of the styles, it isn’t very convenient either because it becomes boilerplate at the top of every web component, which you have to remember to add in order for styles to work properly.
Due to the need for extensive boilerplate, I don’t believe that @apply rules would be a good solution for sharing styles between web components. They are a great solution for theming, though.
According to the web component specification42, browsers ignore any <link rel="stylesheet"> tags in the shadow DOM, treating them just like they would inside of a document fragment. This prevented us from being able to link in any shared styles in our web components, which was unfortunate — that is, until a few months ago, when the Web Components Working Group proposed that <link rel="stylesheet"> tags should work in the shadow DOM43. After only a week of discussion, they all agreed that they should, and a few days later they added it to the HTML specification44.
If that sounds a little too quick for the working group to agree on a specification, that’s because it wasn’t a new proposal. Making link tags work in the shadow DOM was actually proposed at least three years ago45, but it was backlogged until they could ensure it wasn’t a problem for performance.
Being able to link in the shared styles is by far the most convenient method for sharing styles between web components. All you would have to do is create the link tag, and all native elements would be styled accordingly, without requiring any additional work.
Of course, the way browser makers implement the feature will determine whether this solution is viable. For this to work properly, link tags would need to be deduplicated, so that multiple web components requesting the same CSS file would cause only one HTTP request. The CSS would also need to be parsed only once, so that each instance of the web component would not have to recompute the shared styles, but would instead reuse the computed styles.
Chrome does both of these49 already. So, if all other browser makers implement it the same way, then link tags working in the shadow DOM would definitely solve the issue of how to share styles between web components.
You might find it hard to believe, since we haven’t even got it yet, but a link tag working in the shadow DOM is not a long-term solution50. Instead, it’s just a short-term solution to get us to the real solution: constructable style sheets.
Constructable style sheets51 are a proposal to allow for the creation of StyleSheet objects in JavaScript through a constructor function. The constructed style sheet could then be added to the shadow DOM through an API, which would allow the shadow DOM to use a set of shared styles.
Unfortunately, this is all I could gather from the proposal. I tried to find out more information about what constructable style sheets were by asking the Web Components Working Group52, but they redirected me to the W3C’s CSS Working Group’s mailing list53, where I asked again, but no one responded. I couldn’t even figure out how the proposal was progressing, because it hasn’t been updated in over two years54.
Even so, the Web Components Working Group uses it55 as the solution56 for sharing styles57 between web components. Hopefully, either the proposal will be updated or the Web Components Working Group will release more information about it and its adoption. Until then, the “long-term” solution seems like it won’t happen in the foreseeable future.
After months of research and testing, I am quite hopeful for the future. It is comforting to know that after years of not having a solution for sharing styles between web components, there are finally answers. Those answers might not be established for a few more years, but at least they are there.
If you want to use a shared style guide to style web components today, either you can not use the shadow DOM and instead create custom elements, or you can use a web component library that polyfills support for sharing styles. Both solutions have their pros and cons, so use whichever works best for your project.
If you decide to wait a while before delving into web components, then in a few years we should have some great solutions for sharing the styles between them. So, keep checking back on how it’s progressing.
Keep in mind a few things if you decide to use custom elements or web components today.
Most importantly, the web component specification is still being actively developed, which means that things can and will change. Web components are still very much the bleeding edge, so be prepared to stay on your toes as you develop with it.
If you decide to use the shadow DOM, know that it is quite slow58 and unperformant59 in polyfilled browsers60. It was for this reason that Polymer’s developers created their shady DOM implementation and made it their default.
Lastly, the webcomponentjs polyfill only supports the version 0 implementation of the shadow DOM and custom elements. A version 1 branch of the polyfill71 will support version 1, but it’s not yet released.
Recently, I decided to rebuild my personal website, because it was six years old and looked — politely speaking — a little bit “outdated.” The goal was to include some information about myself, a blog area, a list of my recent side projects, and upcoming events.
As I do client work from time to time, there was one thing I didn’t want to deal with — databases! Previously, I built WordPress sites for everyone who wanted me to. The programming part was usually fun for me, but the releases, moving of databases to different environments, and actual publishing, were always annoying. Cheap hosting providers only offer poor web interfaces to set up MySQL databases and an FTP access to upload files was always the worst part. I didn’t want to deal with this for my personal website.
So the requirements I had for the redesign were:
An up-to-date technology stack based on JavaScript and frontend technologies.
A content management solution to edit content from anywhere.
A good performing site with fast results.
In this article I want to show you what I built and how my website surprisingly turned out to be my daily companion.
Publishing things on the web seems to be easy. Pick a content management system (CMS) that provides a WYSIWYG editor (What You See Is What You Get) for every page that’s needed and all the editors can manage the content easily. That’s it, right?
After building several client websites, ranging from small cafés to growing startups, I figured out that the holy WYSIWYG editor is not always the silver bullet we’re all looking for. These interfaces aim to make building websites easy, but here comes the point:
To build and edit the content of a website without constantly breaking it, you have to have intimate knowledge of HTML and at least understand a tiny bit of CSS. That’s not something you can expect from your editors.
I’ve seen horrible complex layouts built with WYSIWYG editors and I can’t begin to name all the situations when everything falls apart because the system is too fragile. These situations lead to fights and discomfort where all parties are blaming each other for something that was inevitable. I always tried to avoid these situations and create comfortable, stable environments for editors to avoid angry emails screaming, “Help! Everything is broken.”
I learned rather quickly that people rarely break things when I split all the needed website content into several chunks, each related to each other without thinking of any representation. In WordPress, this can be achieved using custom post types. Each custom post type can include several properties with their own easy to grasp text field. I buried the concept of thinking in pages completely.
My job was to connect the content pieces and build web pages out of these content blocks. This meant that editors were only able to do little, if any, visual changes on their websites. They were responsible for the content and only the content. Visual changes had to be done by me – not everyone could style the site, and we could avoid a fragile environment. This concept felt like a great trade-off and was usually well received.
Later, I discovered that what I was doing was defining a content model. Rachel Lovinger defines, in her excellent article “Content Modelling: A Master Skill2,” a content model as follows:
A content model documents all the different types of content you will have for a given project. It contains detailed definitions of each content type’s elements and their relationships to each other.
Beginning with content modeling worked fine for most clients, except for one.
“Stefan, I’m not defining your database schema!” Link
The idea of this one project was to build a massive website that should create a lot of organic traffic by providing tons of content – in all variations displayed across several different pages and places. I set up a meeting to discuss our strategy to approach this project.
I wanted to define all the pages and content models that should be included. It didn’t matter what tiny widget or what sidebar the client had in mind, I wanted it to be clearly defined. My goal was to create a solid content structure that makes it possible to provide an easy-to-use interface for the editors and provides reusable data to display it in any thinkable format.
It turned out, the idea of this project was not very clear, and I couldn’t get answers to all of my questions. The project lead didn’t understand that we should start with proper content modeling (not design and development). For him, this was just a ton of pages. Duplicated content and huge text areas to add a massive amount of text, didn’t seem to be a problem. In his mind, the questions I had about structure were technical, and they shouldn’t have to worry about them. To make a long story short, I didn’t do the project.
The important thing is, content modeling is not about databases.
It’s about making your content accessible and future-proof. If you can’t define the needs for your content at project kick-off, it will be very hard, if not impossible, to reuse it later on.
Proper content modeling is the key to present and future websites.
It was clear that I wanted to follow a good content modeling for my site as well. However, there was one more thing. I didn’t want to deal with the storage layer to build my new website, so I decided to use Contentful3, a headless CMS, on which (full disclaimer!) I’m currently working on. “Headless” means that this service offers a web interface to manage the content in the cloud and it provides an API which will give me my data back in JSON format. Choosing this CMS helped me be productive right away as I had an API available in minutes and I did not have to deal with any infrastructural setup. Contentful also provides a free plan4 which is perfect for small projects, like my personal website.
An example query to get all blog posts looks like this:
The great part about Contentful is that it is great at content modeling, which I required. Using the provided web interface, I can define all the needed content pieces quickly. The definition of a particular content model in Contentful is called a content type. A great thing to point out here is the ability to model relationships between content items. For example, I can easily connect an author with a blog post. This can result in structured data trees, which are perfect to reuse for various use cases.
So, I set up my content model without thinking about any pages I may want to build in the future.
The next step was to figure out what I wanted to do with this data. I asked a designer I knew, and he came up with an index page of the website with the following structure.
Now came the tricky part. So far, I didn’t have to deal with storage and databases, which was a great achievement for me. So, how can I build my website when I only have an API available?
My first approach was the do-it-yourself approach. I started writing a simple Node.js script which would retrieve the data and render some HTML out of it.
Rendering all the HTML files upfront fulfilled one of my main requirements. Static HTML can be served really fast.
So, let’s have a look at the script I used.
'use strict'; const contentful = require('contentful'); const template = require('lodash.template'); const fs = require('fs'); // create contentful client with particular credentials const client = contentful.createClient({ space: 'your_space_id', accessToken: 'your_token' }); // cache templates to not read // them over and over again const TEMPLATES = { index : template(fs.readFileSync(`${__dirname}/templates/index.html`)) }; // fetch all the data Promise.all([ // get posts client.getEntries({content_type: 'content_type_post_id'}), // get events client.getEntries({content_type: 'content_type_event_id'}), // get projects client.getEntries({content_type: 'content_type_project_id'}), // get talk client.getEntries({content_type: 'content_type_talk_id'}), // get specific person client.getEntries({'sys.id': 'person_id'}) ]) .then(([posts, events, projects, talks, persons]) => { const renderedHTML = TEMPLATES.index({ posts, events, projects, talks, person : persons.items[0] }) fs.writeFileSync(`${__dirname}/build/index.html`, renderedHTML); console.log('Rendered HTML'); }) .catch(console.error);
This worked fine. I could build my desired website in a completely flexible way, making all the decisions about the file structure and functionality. Rendering different page types with completely different data sets was no problem at all. Everybody who has fought against rules and structure of an existing CMS that ships with HTML rendering knows that complete freedom can be an excellent thing. Especially, when the data model becomes more complex over time including many relations — flexibility pays off.
In this Node.js script, a Contentful SDK10 client is created and all the data is fetched using the client method getEntries. All provided methods of the client are promise-driven, which makes it easy to avoid deeply nested callbacks. For templating, I decided to use lodash’s templating engine. Finally, for file reading and writing, Node.js offers the native fs module, which then is used to read the templates and write the rendered HTML.
However, there was one downside to this approach; it was very bare-bones. Even when this method was completely flexible, it felt like reinventing the wheel. What I was building was basically a static site generator, and there are plenty of them out there already. It was time to start all over again.
Famous static site generators, for example, Jekyll or Middleman, usually deal with Markdown files which will be rendered to HTML. Editors work with these, and the website is built using a CLI command. This approach was failing one of my initial requirements, though. I wanted to be able to edit the site wherever I was, not relying on files sitting on my private computer.
My first idea was to render these Markdown files using the API. Although this would have worked, it didn’t feel right. Rendering Markdown files to transform to HTML later were still two steps not offering a big benefit compared to my initial solution.
Fortunately, there are Contentful integrations, for e.g. Metalsmith11 and Middleman12. I decided on Metalsmith for this project, as it’s written in Node.js and I didn’t want to bring in a Ruby dependency.
Metalsmith transforms files from a source folder and renders them in a destination folder. These files don’t necessarily have to be Markdown files. You can also use it for transpiling Sass or optimizing your images. There are no limits, and it is really flexible.
Using the Contentful integration, I was able to define some source files which were taken as configuration files and could then fetch everything needed from the API.
--- title: Blog contentful: content_type: content_type_id entry_filename_pattern: ${ fields.slug } entry_template: article.html order: '-fields.date' filter: include: 5 layout: blog.html description: Recent articles by Stefan Judis. ---
This example configuration renders the blog post area with a parent blog.html file, including the response of the API request, but also renders several child pages using the article.html template. File names for the child pages are defined via entry_filename_pattern.
As you see, with something like this, I can build up my pages easily. This setup worked perfectly to ensure all the pages were dependent on the API.
The only missing part was to connect the site with the CMS service and to make it re-render when any content was edited. The solution for this problem – webhooks, which you might be familiar with already if you are using services like GitHub.
Webhooks are requests made by software as a service to a previously defined endpoint which notify you that something has happened. GitHub, for example, can ping you back when someone opened a pull request in one of your repos. Regarding content management, we can apply the same principle here. Whenever something happens with the content, ping an endpoint and make a particular environment react to it. In our case, this would mean to re-render the HTML using metalsmith.
To accept webhooks I also went with a JavaScript solution. My choice hosting provider (Uberspace13) makes it possible to install Node.js and use JavaScript on the server side.
const http = require('http'); const exec = require('child_process').exec; const server = http.createServer((req, res) => { res.setHeader('Content-Type', 'text/plain'); // check for secret header // to not open up this endpoint for everybody if (req.headers.secret === ‘YOUR_SECRET') { res.end('ok'); // wait for the CDN to // invalidate the data setTimeout(() => { // execute command exec('npm start', { cwd: __dirname }, (error) => { if (error) { return console.log(error); } console.log('Rebuilt success'); }); }, 1000 * 120 ); } else { res.end('Not allowed'); } }); console.log('Started server at 8000'); server.listen(8000);
This scripts starts a simple HTTP server on port 8000. It checks incoming requests for a proper header to make sure that it’s the webhook from Contentful. If the request is confirmed as the webhook, the predefined command npm start is executed to re-render all the HTML pages. You might wonder why there is a timeout in place. This is required to pause actions for a moment until the data in the cloud is invalidated because the stored data is served from a CDN.
Depending on your environment this HTTP server may not be accessible to the internet. My site is served using an apache server, so I needed to add an internal rewrite rule to make the running node server accessible to the internet.
Being on the road is an important part of my life, so it was necessary to have information, such as the location of a given venue or which hotel I booked, right at my fingertips – usually stored in a Google spreadsheet. Now, the information was spread over a spreadsheet, several emails, my calendar, as well as on my website.
I had to admit, I created a lot of data duplication in my daily flow.
I dreamed of a single source of truth, (preferably on my phone) to quickly see what events were coming up, but also get additional information about hotels and venues. The events listed on my website didn’t have all the information at this point, but it is really easy to add new fields to a content type in Contentful. So, I added the needed fields to the “Event” content type.
Putting this information into my website CMS was never my intention, as it shouldn’t be displayed online, but having it accessible via an API made me realize that I could now do completely different things with this data.
Building apps for mobile has been a topic for years now, and there are several approaches to this. Progressive Web Apps (PWA) are an especially hot topic these days. Using Service Workers16 and a Web App Manifest17, it is possible to build complete app-like experiences going from a home screen icon to managed offline behavior using web technologies.
There is one downside to mention. Progressive Web Apps are on the rise, but they are not completely there yet. Service Workers, for example, are not supported on Safari today and only “under consideration” from Apple’s side18 so far. This was a deal-breaker for me as I wanted to have an offline-capable app on iPhones, too.
So I looked for alternatives. A friend of mine was really into NativeScript and kept telling me about this fairly new technology. NativeScript is an open source framework for building truly native mobile apps with JavaScript, so I decided to give it a try.
The setup of NativeScript takes a while because you have to install a lot of things to develop for native mobile environments. You’ll be guided through the installation process when you install the NativeScript command line tool for the first time using npm install nativescript -g.
Then, you can use scaffolding commands to set up new projects:
tns create MyNewApp
However, this is not what I did. I was scanning the documentation and came across a sample groceries management app19 built in NativeScript. So I took this app, dug into the code, and modified it step by step, fitting it to my needs.
I don’t want to dive too deep into the process, but to build a good looking list with all the information I wanted, didn’t take long.
NativeScript plays really well together with Angular 2, which I didn’t want to try this time as discovering NativeScript itself felt big enough. In NativeScript you have to write “Views.” Each view consists of an XML file defining the base layout and optional JavaScript and CSS. All these are defined in one folder per view.
Rendering a simple list can be achieved with an XML template like this:
List.xml
<!-- call JavaScript function when ready --> <Page loaded="loaded"> <ActionBar title="All Travels" /> <!-- make it scrollable when going too big --> <ScrollView> <!-- iterate over the entries in context --> <ListView items="{{ entries }}"> <ListView.itemTemplate> <Label text="{{ fields.name }}" textWrap="true"/> </ListView.itemTemplate> </ListView> </ScrollView> </Page>
The first thing happening here is defining a page element. Inside of this page, I defined an ActionBar to give it the classic Android look as well as a proper headline. Building things for native environments can be a bit tricky sometimes. For example, to achieve working scroll behavior you have to use a ‘ScrollView.’ The last thing is to then, simply iterate over my events using a ListView. Overall, it felt pretty straightforward!
But where are these entries coming from that are used in the view? It turns out that there is a shared context object that can be used for that. When reading the XML for the view, you may have noticed already that the page has a loaded attribute set. By setting this attribute, I tell the view to call a particular JavaScript function when the page is loaded.
This JavaScript function is defined in the depending JS file. It can be made accessible by simply exporting it using exports.something. To add the data binding, all we have to do is to set a new Observable to the page property bindingContext. Observables in NativeScript emit propertyChange events which are needed to react to data changes inside of the views, but you don’t have to worry about that, as it works out of the box.
List.js
const context = new Observable({ entries: null}); const fetchModule = require('fetch'); // export loaded to be called from // List.xml when everything is loaded exports.loaded = (args) => { const page = args.object; page.bindingContext = context; fetchModule.fetch( `https://cdn.contentful.com/spaces/${config.space}/entries?access_token=${config.cda.token}&content_type=event&order=fields.start`, { method: "GET", headers: { 'Content-Type': 'application/json' } } ) .then(response => response.json()) .then(response => context.set('entries', response.items)); }
The last thing is to fetch the data and set it to the context. This can be done by using the NativeScript fetch module. Here, you can see the result.
So, as you can see — building a simple list using NativeScript is not really hard. I later extended the app with another view as well as additional functionality to open given addresses in Google Maps and web views to look at the event websites.
One thing to point out here is, NativeScript is still pretty new, which means that the plugins found on npm usually do not have a lot of downloads or stars on GitHub. This irritated me at first, but I used several native components (nativescript-floatingactionbutton23, nativescript-advanced-webview24 and nativescript-pulltorefresh25) which helped me achieve a native experience and all worked perfectly fine.
You can see the improved result here:
The more functionality I put into this app, the more I liked it and the more I used it. The best part is, I could get rid of data duplication, managing the data all in one place while, being flexible enough to display it for various use cases.
Pages Are Yesterday: Long Live Structured Content! Link
Building this app showed me once more that the principle of having data in page format is a thing of the past. We don’t know where our data will go — we have to be ready for an unlimited number of use cases.
Looking back, what I achieved is:
Having a content management system in the cloud
Not having to deal with database maintenance
A complete JavaScript technology stack
Having an efficient static website
Having an Android app to access my content every time and everywhere
And the most important part:
Having my content structured and accessible helped me to improve my daily life. Link
This use case might look trivial to you right now, but when you think of the products you build every day — there are always more use cases for your content on different platforms. Today, we accept that mobile devices are finally overtaking the old school desktop environments, but platforms like cars, watches and even fridges are already waiting for their spotlight. I can not even think of the use cases that will come.
So, let’s try to be ready and put structured content in the middle because at the end it’s not about database schemas — it’s about building for the future.
Have you ever wondered what it takes to create a SpriteKit1 game? Do buttons seem like a bigger task than they should be? Ever wonder how to persist settings in a game? Game-making has never been easier on iOS since the introduction of SpriteKit. In part three of this three-part series, we will finish up our RainCat game and complete our introduction to SpriteKit.
If you missed out on the previous lesson52, you can catch up by getting the code on GitHub3. Remember that this tutorial requires Xcode 8 and Swift 3.
This is lesson three in our RainCat journey. In the previous lesson52, we had a long day going though some simple animations, cat behaviors, quick sound effects and background music.
We need a way to keep score. To do this, we can create a heads-up display (HUD). This will be pretty simple; it will be an SKNode that contains the score and a button to quit the game. For now, we will just focus on the score. The font we will be using is Pixel Digivolve, which you can get at Dafont.com7. As with using images or sounds that are not yours, read the font’s license before using it. This one states that it is free for personal use, but if you really like the font, you can donate to the author from the page. You can’t always make everything yourself, so giving back to those who have helped you along the way is nice.
Next, we need to add the custom font to the project. This process can be tricky the first time.
Download and move the font into the project folder, under a “Fonts” folder. We’ve done this a few times in the previous lessons, so we’ll go through this process a little more quickly. Add a group named Fonts to the project, and add the Pixel digivolve.otf file.
Now comes the tricky part. If you miss this part, you probably won’t be able to use the font. We need to add it to our Info.plist file. This file is in the left pane of Xcode. Click it and you will see the property list (or plist). Right-click on the list, and click “Add Row.”
When the new row comes up, enter in the following:
Fonts provided by application
Then, under Item 0, we need to add our font’s name. The plist should look like the following:
The font should be ready to use! We should do a quick test to make sure it works as intended. Move to GameScene.swift, and in sceneDidLoad add the following code at the top of the function:
If it works, then you’ve done everything correctly. If not, then something is wrong. Code With Chris has a more in-depth troubleshooting guide11, but note that it is for an older version of Swift, so you will have to make minor tweaks to bring it up to Swift 3.
Now that we can load in custom fonts, we can start on our HUD. Delete the “Hello World” label, because we only used it to make sure our font loads. The HUD will be an SKNode, acting like a container for our HUD elements. This is the same process we followed when creating the background node in lesson one.
Create the HudNode.swift file using the usual methods, and enter the following code:
import SpriteKit class HudNode : SKNode { private let scoreKey = "RAINCAT_HIGHSCORE" private let scoreNode = SKLabelNode(fontNamed: "PixelDigivolve") private(set) var score : Int = 0 private var highScore : Int = 0 private var showingHighScore = false /// Set up HUD here. public func setup(size: CGSize) { let defaults = UserDefaults.standard highScore = defaults.integer(forKey: scoreKey) scoreNode.text = "(score)" scoreNode.fontSize = 70 scoreNode.position = CGPoint(x: size.width / 2, y: size.height - 100) scoreNode.zPosition = 1 addChild(scoreNode) } /// Add point. /// - Increments the score. /// - Saves to user defaults. /// - If a high score is achieved, then enlarge the scoreNode and update the color. public func addPoint() { score += 1 updateScoreboard() if score > highScore { let defaults = UserDefaults.standard defaults.set(score, forKey: scoreKey) if !showingHighScore { showingHighScore = true scoreNode.run(SKAction.scale(to: 1.5, duration: 0.25)) scoreNode.fontColor = SKColor(red:0.99, green:0.92, blue:0.55, alpha:1.0) } } } /// Reset points. /// - Sets score to zero. /// - Updates score label. /// - Resets color and size to default values. public func resetPoints() { score = 0 updateScoreboard() if showingHighScore { showingHighScore = false scoreNode.run(SKAction.scale(to: 1.0, duration: 0.25)) scoreNode.fontColor = SKColor.white } } /// Updates the score label to show the current score. private func updateScoreboard() { scoreNode.text = "(score)" } }
Before we do anything else, open up Constants.swift and add the following line to the bottom of the file — we will be using it to retrieve and persist the high score:
let ScoreKey = "RAINCAT_HIGHSCORE"
In the code, we have five variables that pertain to the scoreboard. The first variable is the actual SKLabelNode, which we use to present the label. Next is our variable to hold the current score; then the variable that holds the best score. The last variable is a boolean that tells us whether we are currently presenting the high score (we use this to establish whether we need to run an SKAction to increase the scale of the scoreboard and to colorize it to the yellow of the floor).
The first function, setup(size:), is there just to set everything up. We set up the SKLabelNode the same way we did earlier. The SKNode class does not have any size properties by default, so we need to create a way to set a size to position our scoreNode label. We’re also fetching the current high score from UserDefaults12. This is a quick and easy way to save small chunks of data, but it isn’t secure. Because we’re not worried about security for this example, UserDefaults is perfectly fine.
In our addPoint(), we’re incrementing the current score variable and checking whether the user has gotten a high score. If they have a high score, then we save that score to UserDefaults and check whether we are currently showing the best score. If the user has achieved a high score, we can animate the size and color of scoreNode.
In the resetPoints() function, we set the current score to 0. We then need to check whether we were showing the high score, and reset the size and color to the default values if needed.
Finally, we have a small function named updateScoreboard. This is an internal function to set the score to scoreNode‘s text. This is called in both addPoint() and resetPoints().
We need to test whether our HUD is working correctly. Move over to GameScene.swift, and add the following line below the foodNode variable at the top of the file:
private let hudNode = HudNode()
Add the following two lines in the sceneDidLoad() function, near the top:
hudNode.setup(size: size) addChild(hudNode)
Then, in the spawnCat() function, reset the points in case the cat has fallen off the screen. Add the following line after adding the cat sprite to the scene:
hudNode.resetPoints()
Next, in the handleCatCollision(contact:) function, we need to reset the score again when the cat is hit by rain. In the switch statement at the end of the function — when the other body is a RainDropCategory — add the following line:
hudNode.resetPoints()
Finally, we need to tell the scoreboard when the user has earned points. At the end of the file in handleFoodHit(contact:), find the following lines up to here:
//TODO increment points print("fed cat")
And replace them with this:
hudNode.addPoint()
Voilà!
You should see the HUD in action. Run around and collect some food. The first time you collect food, you should see the score turn yellow and grow in scale. When you see this happen, let the cat get hit. If the score resets, then you’ll know you are on the right track!
That’s right, we are moving to another scene! In fact, when completed, this will be the first screen of our app. Before you do anything else, open up Constants.swift and add the following line to the bottom of the file — we will be using it to retrieve and persist the high score:
let ScoreKey = "RAINCAT_HIGHSCORE"
Create the new scene, place it under the “Scenes” folder, and call it MenuScene.swift. Enter the following code in the MenuScene.swift file:
import SpriteKit class MenuScene : SKScene { let startButtonTexture = SKTexture(imageNamed: "button_start") let startButtonPressedTexture = SKTexture(imageNamed: "button_start_pressed") let soundButtonTexture = SKTexture(imageNamed: "speaker_on") let soundButtonTextureOff = SKTexture(imageNamed: "speaker_off") let logoSprite = SKSpriteNode(imageNamed: "logo") var startButton : SKSpriteNode! = nil var soundButton : SKSpriteNode! = nil let highScoreNode = SKLabelNode(fontNamed: "PixelDigivolve") var selectedButton : SKSpriteNode? override func sceneDidLoad() { backgroundColor = SKColor(red:0.30, green:0.81, blue:0.89, alpha:1.0) //Set up logo - sprite initialized earlier logoSprite.position = CGPoint(x: size.width / 2, y: size.height / 2 + 100) addChild(logoSprite) //Set up start button startButton = SKSpriteNode(texture: startButtonTexture) startButton.position = CGPoint(x: size.width / 2, y: size.height / 2 - startButton.size.height / 2) addChild(startButton) let edgeMargin : CGFloat = 25 //Set up sound button soundButton = SKSpriteNode(texture: soundButtonTexture) soundButton.position = CGPoint(x: size.width - soundButton.size.width / 2 - edgeMargin, y: soundButton.size.height / 2 + edgeMargin) addChild(soundButton) //Set up high-score node let defaults = UserDefaults.standard let highScore = defaults.integer(forKey: ScoreKey) highScoreNode.text = "(highScore)" highScoreNode.fontSize = 90 highScoreNode.verticalAlignmentMode = .top highScoreNode.position = CGPoint(x: size.width / 2, y: startButton.position.y - startButton.size.height / 2 - 50) highScoreNode.zPosition = 1 addChild(highScoreNode) } }
Because this scene is relatively simple, we won’t be creating any special classes. Our scene will consist of two buttons. These could be (and possibly deserve to be) their own class of SKSpriteNodes, but because they are different enough, we will not need to create new classes for them. This is an important tip for when you build your own game: You need to be able to determine where to stop and refactor code when things get complex. Once you’ve added more than three or four buttons to a game, it might be time to stop and refactor the menu button’s code into its own class.
The code above isn’t doing anything special; it is setting the positions of four sprites. We are also setting the scene’s background color, so that the whole background is the correct value. A nice tool to generate color codes from HEX strings for Xcode is UI Color15. The code above is also setting the textures for our button states. The button to start the game has a normal state and a pressed state, whereas the sound button is a toggle. To simplify things for the toggle, we will be changing the alpha value of the sound button upon the user’s press. We are also pulling and setting the high-score SKLabelNode.
Our MenuScene is looking pretty good. Now we need to show the scene when the app loads. Move to GameViewController.swift and find the following line:
let sceneNode = GameScene(size: view.frame.size)
Replace it with this:
let sceneNode = MenuScene(size: view.frame.size)
This small change will load MenuScene by default, instead of GameScene.
Buttons can be tricky in SpriteKit. Plenty of third-party options are available (I even made one myself), but in theory you only need to know the three touch methods:
touchesBegan(_ touches: with event:)
touchesMoved(_ touches: with event:)
touchesEnded(_ touches: with event:)
We covered this briefly when updating the umbrella, but now we need to know the following: which button was touched, whether the user released their tap or clicked that button, and whether the user is still touching it. This is where our selectedButton variable comes into play. When a touch begin, we can capture the button that the user started clicking with that variable. If they drag outside the button, we can handle this and give the appropriate texture to it. When they release the touch, we can then see whether they are still touching inside the button. If they are, then we can apply the associated action to it. Add the following lines to the bottom of MenuScene.swift:
override func touchesBegan(_ touches: Set, with event: UIEvent?) { if let touch = touches.first { if selectedButton != nil { handleStartButtonHover(isHovering: false) handleSoundButtonHover(isHovering: false) } // Check which button was clicked (if any) if startButton.contains(touch.location(in: self)) { selectedButton = startButton handleStartButtonHover(isHovering: true) } else if soundButton.contains(touch.location(in: self)) { selectedButton = soundButton handleSoundButtonHover(isHovering: true) } } } override func touchesMoved(_ touches: Set, with event: UIEvent?) { if let touch = touches.first { // Check which button was clicked (if any) if selectedButton == startButton { handleStartButtonHover(isHovering: (startButton.contains(touch.location(in: self)))) } else if selectedButton == soundButton { handleSoundButtonHover(isHovering: (soundButton.contains(touch.location(in: self)))) } } } override func touchesEnded(_ touches: Set, with event: UIEvent?) { if let touch = touches.first { if selectedButton == startButton { // Start button clicked handleStartButtonHover(isHovering: false) if (startButton.contains(touch.location(in: self))) { handleStartButtonClick() } } else if selectedButton == soundButton { // Sound button clicked handleSoundButtonHover(isHovering: false) if (soundButton.contains(touch.location(in: self))) { handleSoundButtonClick() } } } selectedButton = nil } /// Handles start button hover behavior func handleStartButtonHover(isHovering : Bool) { if isHovering { startButton.texture = startButtonPressedTexture } else { startButton.texture = startButtonTexture } } /// Handles sound button hover behavior func handleSoundButtonHover(isHovering : Bool) { if isHovering { soundButton.alpha = 0.5 } else { soundButton.alpha = 1.0 } } /// Stubbed out start button on click method func handleStartButtonClick() { print("start clicked") } /// Stubbed out sound button on click method func handleSoundButtonClick() { print("sound clicked") }
This is simple button-handling for our two buttons. In touchesBegan(_ touches: with events:), we start off by checking whether we have any currently selected buttons. If we do, we need to reset the state of the button to unpressed. Then, we need to check whether any button is pressed. If one is pressed, it will show the highlighted state for the button. Then, we set selectedButton to the button for use in the other two methods.
In touchesMoved(_ touches: with events:), we check which button was originally touched. Then, we check whether the current touch is still within the bounds of selectedButton, and we update the highlighted state from there. The startButton‘s highlighted state changes the texture to the pressed-state’s texture, where the soundButton‘s highlighted state has the alpha value of the sprite set to 50%.
Finally, in touchesEnded(_ touches: with event:), we check again which button is selected, if any, and then whether the touch is still within the bounds of the button. If all cases are satisfied, we call handleStartButtonClick() or handleSoundButtonClick() for the correct button.
Now that we have the basic button behavior down, we need an event to trigger when they are clicked. The easier button to implement is startButton. On click, we only need to present the GameScene. Update handleStartButtonClick() in the MenuScene.swift function to the following code:
If you run the app now and press the button, the game will start!
Now we need to implement the mute toggle. We already have a sound manager, but we need to be able to tell it whether muting is on or off. In Constants.swift, we need to add a key to persist when muting is on. Add the following line:
let MuteKey = "RAINCAT_MUTED"
We will use this to save a boolean value to UserDefaults. Now that this is set up, we can move into SoundManager.swift. This is where we will check and set UserDefaults to see whether muting is on or off. At the top of the file, under the trackPosition variable, add the following line:
private(set) var isMuted = false
This is the variable that the main menu (and anything else that will play sound) checks to determine whether sound is allowed. We initialize it as false, but now we need to check UserDefaults to see what the user wants. Replace the init() function with the following:
private override init() { //This is private, so you can only have one Sound Manager ever. trackPosition = Int(arc4random_uniform(UInt32(SoundManager.tracks.count))) let defaults = UserDefaults.standard isMuted = defaults.bool(forKey: MuteKey) }
Now that we have a default value for isMuted, we need the ability to change it. Add the following code to the bottom of SoundManager.swift:
This method will toggle our muted variable, as well as update UserDefaults. If the new value is not muted, playback of the music will begin; if the new value is muted, playback will not begin. Otherwise, we will stop the current track from playing. After this, we need to edit the if statement in startPlaying().
Find the following line:
if audioPlayer == nil || audioPlayer?.isPlaying == false {
This toggles the sound in SoundManager, checks the result and then appropriately sets the texture to show the user whether the sound is muted or not. We are almost done! We only need to set the initial texture of the button on launch. In sceneDidLoad(), find the following line:
Now that the music is hooked up, we can move to CatSprite.swift to disable the cat meowing when muting is on. In the hitByRain(), we can add the following if statement after removing the walking action:
if SoundManager.sharedInstance.isMuted { return }
This statement will return whether the user has muted the app. Because of this, we will completely ignore our currentRainHits, maxRainHits and meowing sound effects.
After all of that, now it is time to try out our mute button. Run the app and verify whether it is playing and muting sounds appropriately. Mute the sound, close the app, and reopen it. Make sure that the mute setting persists. Note that if you just mute and rerun the app from Xcode, you might not have given enough time for UserDefaults to save. Play the game, and make sure the cat never meows when you are muted.
Now that we have the first type of button for the main menu, we can get into some tricky business by adding the quit button to our game scene. Some interesting interactions can come up with our style of game; currently, the umbrella will move to wherever the user touches or moves their touch. Obviously, the umbrella moving to the quit button when the user is attempting to exit the game is a pretty poor user experience, so we will attempt to stop this from happening.
The quit button we are implementing will mimic the start game button that we added earlier, with much of the process staying the same. The change will be in how we handle touches. Get your quit_button and quit_button_pressed assets into the Assets.xcassets file, and add the following code to the HudNode.swift file:
private var quitButton : SKSpriteNode! private let quitButtonTexture = SKTexture(imageNamed: "quit_button") private let quitButtonPressedTexture = SKTexture(imageNamed: "quit_button_pressed")
This will handle our quitButton reference, along with the textures that we will set for the button states. To ensure that we don’t inadvertently update the umbrella while trying to quit, we need a variable that tells the HUD (and the game scene) that we are interacting with the quit button and not the umbrella. Add the following code below the showingHighScore boolean variable:
private(set) var quitButtonPressed = false
Again, this is a variable that only the HudNode can set but that other classes can check. Now that our variables are set up, we can add in the button to the HUD. Add the following code to the setup(size:) function:
The code above will set the quit button with the texture of our non-pressed state. We’re also setting the position to the upper-right corner and setting the zPosition to a high number in order to force it to always draw on top. If you run the game now, it will show up in GameScene, but it will not be clickable yet.
Now that the button has been positioned, we need to be able to interact with it. Right now, the only place where we have interaction in GameScene is when we are interacting with umbrellaSprite. In our example, the HUD will have priority over the umbrella, so that users don’t have to move the umbrella out of the way in order to exit. We can create the same functions in HudNode.swift to mimic the touch functionality in GameScene.swift. Add the following code to HudNode.swift:
func touchBeganAtPoint(point: CGPoint) { let containsPoint = quitButton.contains(point) if quitButtonPressed && !containsPoint { //Cancel the last click quitButtonPressed = false quitButton.texture = quitButtonTexture } else if containsPoint { quitButton.texture = quitButtonPressedTexture quitButtonPressed = true } } func touchMovedToPoint(point: CGPoint) { if quitButtonPressed { if quitButton.contains(point) { quitButton.texture = quitButtonPressedTexture } else { quitButton.texture = quitButtonTexture } } } func touchEndedAtPoint(point: CGPoint) { if quitButton.contains(point) { //TODO tell the gamescene to quit the game } quitButton.texture = quitButtonTexture }
The code above is a lot like the code that we created for MenuScene. The difference is that there is only one button to keep track of, so we can handle everything within these touch methods. Also, because we will know the location of the touch in GameScene, we can just check whether our button contains the touch point.
Move over to GameScene.swift, and replace the touchesBegan(_ touches with event:) and touchesMoved(_ touches: with event:) methods with the following code:
override func touchesBegan(_ touches: Set, with event: UIEvent?) { let touchPoint = touches.first?.location(in: self) if let point = touchPoint { hudNode.touchBeganAtPoint(point: point) if !hudNode.quitButtonPressed { umbrellaNode.setDestination(destination: point) } } } override func touchesMoved(_ touches: Set, with event: UIEvent?) { let touchPoint = touches.first?.location(in: self) if let point = touchPoint { hudNode.touchMovedToPoint(point: point) if !hudNode.quitButtonPressed { umbrellaNode.setDestination(destination: point) } } } override func touchesEnded(_ touches: Set, with event: UIEvent?) { let touchPoint = touches.first?.location(in: self) if let point = touchPoint { hudNode.touchEndedAtPoint(point: point) } }
Here, each method handles everything in pretty much the same way. We’re telling the HUD that the user has interacted with the scene. Then, we check whether the quit button is currently capturing the touches. If it is not, then we move the umbrella. We’ve also added the touchesEnded(_ touches: with event:) function to handle the end of the click for the quit button, but we are still not using it for umbrellaSprite.
Now that we have a button, we need a way to have it affect GameScene. Add the following line to the top of HudeNode.swift:
var quitButtonAction : (() -> ())?
This is a generic closure19 that has no input and no output. We will set this with code in the GameScene.swift file and call it when we click the button in HudNode.swift. Then, we can replace the TODO in the code we created earlier in the touchEndedAtPoint(point:) function with this:
if quitButton.contains(point) && quitButtonAction != nil { quitButtonAction!() }
Now, if we set the quitButtonAction closure, it will be called from this point.
To set up the quitButtonAction closure, we need to move over to GameScene.swift. In sceneDidLoad(), we can replace our HUD setup with the following code:
Run the app, press play, and then press quit. If you are back at the main menu, then your quit button is working as intended. In the closure that we created, we initialized a transition to the MenuScene. And we set this closure to the HUD node to run when the quit button is clicked. Another important line here is when we set the quitButtonAction to nil. The reason for this is that a retain cycle is occurring. The scene is holding a reference to the HUD where the HUD is holding a reference to the scene. Because there is a reference to both objects, neither will be disposed of when it comes time for garbage collection. In this case, every time we enter and leave GameScene, another instance of it will be created and never released. This is bad for performance, and the app will eventually run out of memory. There are a number of ways to avoid this, but in our case we can just remove the reference to GameScene from the HUD, and the scene and HUD will be terminated once we go back to the MenuScene. Krakendev has a deeper explanation20 of reference types and how to avoid these cycles.
Now, move to GameViewController.swift, and remove or comment out the following three lines of code:
With the debugging data out of the way, the game is looking really good! Congratulations: We are currently into beta! Check out the final code from today on GitHub21.
This is the final lesson of a three-part tutorial, and if you made it this far, you just did a lot of work on your game. In this tutorial, you went from a scene that had absolutely nothing in it, to a completed game. Congrats! In lesson one22, we added the floor, raindrops, background and umbrella sprites. We also played around with physics and made sure that our raindrops don’t pile up. We started out with collision detection and worked on culling nodes so that we would not run out of memory. We also added some user interaction by allowing the umbrella to move around towards where the user touches on the screen.
In lesson two23, we added the cat and food, along with custom spawning methods for each of them. We updated our collision detection to allow for the cat and food sprites. We also worked on the movement of the cat. The cat gained a purpose: Eat every bit of food available. We added simple animation for the cat and added custom interactions between the cat and the rain. Finally, we added sound effects and music to make it feel like a complete game.
In this last lesson, we created a heads-up display to hold our score label, as well as our quit button. We handled actions across nodes and enabled the user to quit with a callback from the HUD node. We also added another scene that the user can launch into and can get back to after clicking the quit button. We handled the process for starting the game and for controlling sound in the game.
We put in a lot of time to get this far, but there is still a lot of work that can go into this game. RainCat continues development still, and it is available in the App Store24. Below is a list of wants and needs to be added. Some of the items have been added, while others are still pending:
Add in icons and a splash screen.
Finalize the main menu (simplified for the tutorial).
Fix bugs, including rogue raindrops and multiple food spawning.
Refactor and optimize the code.
Change the color palette of the game based on the score.
Update the difficulty based on the score.
Animate the cat when food is right above it.
Integrate Game Center.
Give credit (including proper credit for music tracks).
Keep track on GitHub25 because these changes will be made in future. If you have any questions about the code, feel free to drop us a line at hello@thirteen23.com26 and we can discuss it. If certain topics get enough attention, maybe we can write another article discussing the topic.
Around a year ago, while working at a digital agency, I was given the objective of streamlining our UX design process. Twelve months later, this article shares my thoughts and experiences on how lean thinking helped to instill efficiencies within our UX design process.
When I arrived at the agency, wireframes were already being created and utilized across a variety of projects. Winning advocates for the production of wireframes was not the issue. All stakeholders (both internally and externally) understood the purpose of wireframes and appreciated their value in shaping and modeling digital experiences.
However, up until this point, rather than dictate a promoted “way of working,” the agency had encouraged UX designers to “do things their way.” While this increased autonomy had once allowed UX designers to work at speed, using their preferred tools and processes, it was now starting to create problems.
When you stepped back and looked across past projects, you could see the different tools and processes in action, ranging from low-fidelity wireframing tools such as Balsamiq1 and Moqups.com2, to mid-high fidelity outputs from tools such as Axure3 and UX Pin4.
For clients undertaking multiple projects, the lack of consistent wireframe deliverables was confusing and disorientating, with the client having to remember multiple URLs and logins while also learning how to navigate the various outputs.
Meanwhile, for the agency, the absence of a standardized UX design process was costly both in time and money. The lack of a shared file structure across projects meant, if resource patterns changed across a project, which they often did, it was hard for new UX designers to pick up where their counterparts had left off. At the same time, many routine tasks were unnecessarily repeated across multiple projects.
It was clear we needed to establish some rules and guidelines to create a more cohesive approach. We needed to set a new direction, and now was the time to start.
Before introducing a new company-wide process to wireframing, I needed to highlight to the UX team where the lack of process was causing us issues and how establishing a standardized process could help.
To ensure buy-in from the wider stakeholder group, I gathered the UX team together and presented them with the challenge:
How can we establish a standard wireframing process that allows us to work at speed, while also improving cross-project consistency?
As the team discussed the issue, I quickly mapped out each key milestone in the wireframing process on a whiteboard. We discussed the potential enhancement opportunities for each milestone. For an enhancement to be accepted, it had to deliver against one of the following criteria:
By introducing a template file, we felt we could help save the UX designer time during a project’s initial set-up. The template file would save the UX designer from having to complete the routine tasks that come with setting up a new project (creating responsive views, grid systems, document structures, changelogs, and so on).
We also felt that creating a template file would define a baseline for what a working project file should look like. We believed the file template would establish a core foundation and structure for the wireframe document and therefore promote cross-project consistency.
The standard wireframe file template we created included elements such as:
Introductory page
To welcome stakeholders to the wireframe, explaining how to navigate the wireframe document and introduce the changelog.
Component master
List of all components and pages grouped into categories, with direct links to lower level pages and components (signposting, forms, and so on).
Document structure
Folders for each type of wireframe page, helping stakeholders easily distinguish page layouts from components and user journeys from sitemaps.
Preset breakpoints and grid systems
Standard responsive wireframe breakpoints and grid systems.
Agreeing on a set of common breakpoints for our template file was perhaps the hardest task to accomplish. After some lengthy debates with the UX team and other internal stakeholders, we came to the following conclusion:
The wireframe’s purpose is to communicate page functionality, visual hierarchy, and interactions. It is not to provide a pixel-perfect representation of the end product.
Project stakeholders are most interested in seeing how the layout would respond across desktop, tablets and mobile devices.
The breakpoints demonstrated within the wireframes would only be representative, and considered again later within the design phase.
After coming to this shared agreement, we collectively decided we would produce wireframes to represent the three common breakpoints stakeholders were interested in (desktop, tablet and mobile). The final breakpoints would then be explored throughout the design phase, as we considered browser trends and the client’s current web analytics data.
We settled on the following grids and breakpoints for our template file:
Benefits: Save time, improve consistency, facilitate rapid working.
The next and most significant enhancement we identified was the idea to introduce our own wireframe design language.
For those of you who are new to design languages, a design language provides a unified set of UX and design rules which promote harmony across various media outputs. Unlike a style guide that creates rules for acceptable fonts, colors, imagery, and tone of voice, a design language, creates rules for UI patterns providing guidance on layout, user input, animation and feedback.
In recent years, design languages have become more popular thanks to the introduction of Google’s Material Design11. However, companies such as the BBC12 and IBM13 have been developing digital assets that adhere to well establish design languages for some time.
Typical elements you would expect to see in a design language are:
Grid systems
Accessibility guidelines
Layout principles
Typography and iconography
Interaction guidelines
UI components
By introducing a design language, we believed we could ensure a consistent look and feel across our wireframes while also reminding UX designers of common responsive UI patterns.
To make it easy for UX designers to align with the design language we wanted to turn our design language into a responsive wireframe library UX designers could use as they work, allowing them to build responsive wireframes at speed.
To define our design language, we slowly started to pull together a list of the commonly used UI patterns across our last four major projects. We then aligned these with our new responsive grid system and built it into a responsive wireframe library.
Our responsive wireframe library quickly became our living and breathing design language. We initially started with a small set of 30 components, however, over time, this has now expanded contain over 100+ responsive UI elements some of which you can see below.
If you are looking to create your own wireframe design language, I would highly recommend getting your whole team involved as early as possible. Taking people’s input early-on in a venture like this can help justify and shape the proposition. It will also assist to ensure the result is a collaborative effort where all UX designers support the end solution.
The key thing to remember here, the objective for a design language is to assist and support UX designers on projects by reminding them of common UI patterns fit for the medium in which content is being digested. Its purpose is not to set hard and fast rules that all projects must follow. Otherwise, all projects would end up looking too similar.
To run a “design language” workshop session, block out a day in your team’s calendar, gather everyone together with some Sharpies, snacks, Post-it notes, Blu-Tack, and paper.
Depending on the size of your audience, split into groups of four to six. Armed with pens and Post-its, sketch out common UI elements you have used on recent web projects, adding a suitable name to your component as you go.
Once you have sketched each component, place it up on the wall. When everyone has completed their components, take some time to sort them into common themes, (i.e. Navigation, Experience, Conversion, Browse) removing any duplicates as you go.
At the end of the workshop, you should have a list of common UI components you can use to build your design language. Initially, try to keep this list concise, with around 20 to 30 components maximum.
From here, you will need a medium to capture your components into a shared location the whole team can access, and allow them to collaborate further and feed in new requirements.
We used a Trello2725 board to communicate all the components we had captured from our workshop and displayed them within their groupings. An additional “Ideas” column was created to provide a space where new components could be added by any team member and would be discussed in upcoming team meetings.
Being able to see things in a consolidated view within our Trello board allowed us to discuss and prioritize which components we would build into our design language. Anything which was not a core requirement was moved to a later phase.
The key thing we communicated to the UX team was that our design language would become a ‘living library’ that extends over time. Therefore, the initial phases of our design language would be an MVP where we would use feedback from the UX team to shape the library when moving forward.
Note:Trello is a great lightweight tool for managing and assigning tasks across small teams. However, if your organization has a JIRA account, I would recommend using this as your primary tool to manage this process. JIRA29 has a lot more functionality than Trello, allowing access to features such as development reports and components requested and added. You can also monitor time spent on jobs which may prove useful for reporting your progress up the management chain.
It was always our intention to build our design language into a UI kit that UX designers could utilize throughout the creative process. To achieve this, we transformed our design language into a “Responsive Axure Widget Library.”
When it comes to selecting the wireframing tool that is right for your business, there are a number factors to consider:
Ease of use
How easy is it for beginners to grasp? Will any training be required? If so, what support and training resources are available?
Accessibility/Scalability
Does it allow multiple UX designers to work in collaboration on a single document? How does it handle large documents with multiple page revisions?
Features
What built-in features does it have? Can you create responsive wireframes, sticky headers, and parallax scrolling?
Fidelity
What does a typical output look like? For example, sketch-based page layouts or high-fidelity interactive prototypes?
We selected Axure as our wireframing tool due to its rich feature set, ability to handle large documents, collaboration capabilities and ability to support third-party widget libraries. Not to mention, all UX designers had experience using the tool.
However, Axure may not be the best choice for your business due to its steep learning curve and licensing arrangement. The key message is when selecting your wireframing tool you should consider your business needs.
Most wireframing tools now support the inclusion of custom libraries UXPin30, Justinmind31 and Sketch32 to name but a few. Sadly Adobe XD33 does not support custom libraries yet, however this feature is expected to make an appearance in the near future, according to Adobe’s Blog34.
The final enhancement for our new process was to create some guidelines for documentation and annotations. In the past, various approaches had been taken to produce documentation depending on the size, scale, and timelines for the project. For example:
In-Page (High-level functionality annotations)
Each page of the wireframe should clearly describe the role of the component(s) within.
Describe each atomic component to the minute detail (what information CMS editors can edit or update, along with limitations, restrictions and responsive behaviours).
Wiki (Functional specifications)
Create wikis that document the entire project from a functional perspective. Include all page and component functionality along with rules around other items, such as web analytics, browser support, roles, permissions, and governance.
When discussing annotations as a team, we felt, despite “low-level functional specification annotations” within the wireframes being seen by project managers as a way to reduce documentation timelines, this often created more problems than it solved.
For a start, it meant the only member of the project team who could access and update the specification was the UX designer who had access to the wireframing tool. Secondly, the document didn’t allow for cross-team collaboration (such as input from other teams, i.e. QA, or Project Management).
After discussing as a team, we agreed when moving forward:
We would only ever use in-page annotations within wireframes to communicate the role of the component.
Atomic, component level specifications would always be delivered via the Functional Specification in a Wiki format.
We would agree on a common structure for our functional specification wikis.
The structure we settled on for our Functional Specification Wiki’s was as follows:
1. Introduction
This is where we introduce stakeholders to the project and explain what content can be found throughout the specification. Typical sub-pages within this section would contain the role of the project, scope of the project, sitemap, and changelong.
2. Page Templates
This is where we grouped all page templates together and identified static versus dynamic pages. For example, content pages versus search results pages or product pages. For each page template, we would describe the role of the page and page-specific functionality as well as a full-page screen grab and a link to the wireframes.
Where a page template used a common component, (i.e., Site Navigation, Breadcrumb, Hero) we would simply link to the component page rather than re-document the component multiple times. Not only did this reduce the documentation, it also allowed stakeholders reviewing the document to dip in and out of the content they needed to review easily.
3. Components
This is where we grouped all UI components together. For each component, we then identified what content fields were available to the CMS user, whether content was manual or automated, and defined validation rules as well as interaction behaviors.
4. Special Considerations
This is where we listed out all other wider topics related to the project that needed to be documented but were not specific to any given page. Typical topics which lived in this section were:
Rethinking our UX workflow from the ground up allowed us to deliver both time and cost savings across the UX team. By introducing key templates wherever possible, we were able to relieve our UX designers from some of the more tedious routine tasks they need to perform on each project, while at the same time, promote cross-project consistency.
An integral part of the new workflow was the introduction of our design language, which has transformed the way we wireframe projects. Introducing the design language has allowed us to work lean, enabling us to build responsive wireframes at speed.
Being able to establish 60%-70% of page layouts more quickly has meant concepts can be demonstrated to stakeholders for feedback much earlier, providing UX designers with more time to obsess over the intricate details of the project that surprise and delight. It is often those little details that get sacrificed when demanding project deadlines loom.
The design language should be used to help shape pages and components in the early phases of a project rather than dictate all component functionality in its entirety.
Each project is unique in its own right. It comes with its own set of users, requirements, expectations, and challenges. In any web project, the early phases bring a lot of uncertainty. The earlier we produce artifacts such as wireframes, the quicker we learn from stakeholders what works, what doesn’t, and more importantly why. It’s this understanding that helps to direct and steer future adaptations along with the end product.
Furthermore, your design language isn’t a “set it and forget it” tool. Your design language should be a living part of the UX design process that changes and adjusts over time as technology changes and new interaction patterns emerge. For this reason, your design language should always adapt over time based on feedback from your UX designers, clients, and users.
Below you can see concepts for a homepage of a news site and landing page of a product focused website (based on Vessyl). Both concepts were produced as responsive wireframes using Axure RP 8.
Being able to leverage the Responsive Axure Library (built as part of introducing a design language) meant these concepts that had previously taken a day to complete could be produced in just one and a half hours. Not only that, they now look consistent, utilizing the same visual presentation for elements, such as images and video.
Being able to produce artifacts rapidly means more time can be spent with the client to discuss initial thoughts on look, feel, layout, and the responsive treatment of components. You can also spend time on smaller details such as like versus commenting functionality, taxonomy, content prioritization, (manually curated vs. automated feeds) and so on.
This is a concept for a news and media-based site that produces article based content across a number of categories, from technology to health and nutrition. The aim of this site is to drive engagement and loyalty, with users expected to return to this site multiple times throughout the week. As such, keeping content fresh and relevant to the end user is key to drive repeat engagements.
This concept is a simplified version of the landing page displayed on Vessyl39. The role of this page is to educate and build interest in the Vessyl product. Remember, this may be the first page users see for the product (as they may be linking from various news or PR sites). Therefore, this page should utilize story-telling principles, as well social proofing, to bring the product to life and make users aware of how using the product will benefit their daily lives.
We shouldn’t let ourselves get distracted by people who work on different projects than we do. If a developer advocate works on a web-based QR code application, for example, their way of tackling things most certainly won’t fit your project. If someone builds a real-time dashboard, their concept won’t relate to the company portfolio website you’re building. Bear in mind that you need to find the best concept, the best technologies, the best solution for your specific project.
Thinking about the right decisions rather than following cool, new trends blindly, is the first step to building responsible web solutions. That’s what we call progressive enhancement. The only subjective matter in this undertaking is you, judging what level of progressive enhancement a solution should have.
Aaron Gustafson wrote a thoughtful piece on why progressive enhancement is not an anti-JavaScript concept3 but a concept of finding the best way to adapt to the nature of the web. It’s a subtle, inclusive concept that takes the environment and its quirks into account.
Microsoft’s Inclusive Design guidelines7 and resources are very helpful to understand how you as a company can take advantage of creating an inclusive product by design.
Tim Kadlec describes8 what a new project called “The Web, Worldwide9” is about and why it’s important for developers and project owners to understand the role of the Internet in various markets. I wrote a similar post this week about choosing browser support in a project10 and why we’re often doing it wrong because we base our assumptions on misleading data.
These fun statistics on HTML and SVG usage11 are really insightful. By analyzing eight million websites, some interesting facts could be discovered: href="javascript:void(0)", for example, is still used massively, and span.button can also be found in a lot of codebases.
Unfortunately, there’s no further source to back up this statement, but Domenic Denicola found out that the Filesystem API might be removed from the specification12 as it turned out that it’s used for incognito mode detection in browsers in 95% of the use cases.
The parallax effect isn’t going away anytime soon, so if we need to make use of it, we should at least do it in the most effective, most performant way. Paul Lewis shares how to achieve that13.
Remy Sharp reports how he got started with React.js and how he finally made Server Side React14 work in his project.
Holger Bartel wrote about the value of attending conferences18 and how different things are in Asia, for example, when compared to other parts of the world.
Christmas is just around the corner, and what better way to celebrate than with some free goodies? We sifted through the web (and our archives) to find holiday-themed icon sets for you that’ll give your creative projects some holiday flair. Perfect for Christmas cards, gift tags, last-minute wrapping paper, or whatever else you can think of.
All icons can be downloaded for free, but please consult their licenses or contact the creators before using them in commercial projects. Reselling a bundle is never cool, though. Have a happy holiday season!
For even more holiday spirit, you might also want to check out the following posts:
Roast turkey, gingerbread men, reindeer, and that comfy Christmas sweater that waits in the back of the closet to be dug out. With 110 icons in total, Anastasia Kolisnichenko’s Christmas icon set4 has everything a Christmas lover’s heart could ask for. The icons are available in AI, PSD, PNG, and EPS formats and you can customize stroke width, size, color, and shape to your liking. The license allows you to use the illustrations for anything you want – think postcards, posters, gift tags – also commercially.
Do you prefer a more minimalistic approach? Then George Neocleous’ festive Christmas icon set7 is for you. It includes 20 vector icons in EPS format, with both color and grayscale versions available. These are free to use without any restrictions in personal and commercial projects. Now, imagine that cute nutcracker on a Christmas card…
With their storybook-like look, Manuela Langella’s icon set10 stirs those familiar warm feelings. In this set, you’ll find 24 icons in total. Among them, Langella has included some unique motifs, such as Santa stuck in a chimney, as well as the obligatory cookies and milk, and stockings hung by the fireplace. The icons come in six formats (AI, PSD, EPS, PDF, SVG, and PNG) and can be customized not only in size, color, and shape but, thanks to full layered Illustrator and Photoshop assets, also assembled in any way you like. Free to use for private and commercial projects.
Another from the creative mind of Manuela Langella, is the Advent icon set13. It features 25 icons to celebrate the Advent season: decoration, food, and even Santa’s little helper is there to join the party. The download includes AI, EPS, SVG, PNG, and PDF formats that you can modify to your liking. A Creative Commons Attribution 3.0 Unported license allows you to use the set in private as well as commercial projects.
RocketTheme’s Christmas icon set16 shines with its love for detail: the little cracks in the gingerbread man, the bubbles on the milk, the chiffon bow wrapped around the present. There are ten icons in the set in total, all of which come as 256×256 PNGs. A Creative Commons Attribution-NoDerivs 3.0 Unported License allows you to use them in both commercial and private projects, but please be sure to always credit the designer.
One set, three styles: IconEden’s three-in-one Christmas set19 comes in a realistic 3D style, a simple shape style, and a button style. The 39 icons are available in vector and pixel format and can be used freely both in private and commercial projects. Talk about versatility!
This fun and cartoonish icon set22 comes from Andrey Stelya. The fresh colors and the unusual way of applying them, by shifting the underlying color layer outside the line art, gives the icons a modern feel. The set includes twelve icons and comes in SVG and PNG (90×90) formats.
Now this is a versatile set! Olha Filipenko created 78 icons in AI format with everything winter and holiday-themed25: sweets, snowflakes, candles, ornaments, even a cute little postage stamp. There are so many ways to use the minimalistic line art with a unique and sophisticated twist.
How about some Christmas cheer as sweet as the icing on the cookies? The icon set28 of Bangkok-based designer Sunbzy is striking with an unusual pastel color palette. The 20 icons can be downloaded for free as an AI file and are as in-demand as Grandma’s cookies!
Explore the snowy mountains, take your friends skiing, or go ice-skating on the frosty lake with Benjamin Bely’s beautiful winter icon set31 that cherishes these outdoor winter moments. The bundle consists of twelve icons in AI format and can be downloaded for free. All the love of those chilly adventures from the warmth of your home.
Pixel time! Anna Sereda’s and Maryan Ivasyk’s icon set34 looks as if it came straight out of a Christmas arcade game. There are 16 little pixel artworks in the bundle for you to use in personal and commercial projects. Available in AI, EPS, PSD, SVG, PNG, and JPEG formats.
This fine small set38 comes from Dasha Ermolova. It includes four motifs – a snow globe, a stocking, a Christmas wreath, and a ball ornament. The EPS is free to download. This is minimalistic line art with a nifty touch.
With her Christmas icon set41, Magda Gogo gives classical motifs, like stockings, Christmas trees, and candy canes, a fresh makeover. There are eight icons in the bundle, and they come in both EPS and AI formats. True classics never go out of style.
Another lovely set of flat illustrations44 comes from Vector4Free. The softly colored motifs range from snowflakes and stockings to a pipe-smoking hipster reindeer. 33 icons are available in AI, EPS, SVG, PSD, and PNG formats. The Creative Commons Attribution 3.0 Unported License allows you to use them for any purpose, including commercially as long as you give appropriate credit.
Even though nature is sparse in winter in many parts of the world, there are little treasures you can find on your walk through the forest: pine cones, fir needles, acorns. To celebrate their beauty, Freepik released an icon set with watercolor leaves and branches47. They are available in AI and EPS formats and can be used for personal and commercial projects as long as you credit the designer.
What would Christmas be without cookies? Anna Zhemchuzhina created a lovely little set of six gingerbread-inspired icons50 that look as if they came freshly baked out of the oven. Available as PSD, these icons look good enough to eat!
Spread the joy! Non-traditional colors and a playful design are at the base of Livi Po’s retro-inspired Christmas icon set53. The ten icons are available in AI format, and a wonderful new take on the truly retro past.
Ever dream of spending the holidays in a cabin in the woods? You go out to choose the Christmas tree, spend time sitting by the fireplace, and drink tea as you watch the snow fall. That’s the feeling that Freepik captures with their Lovely Christmas Icon Set56. Classic, calm Christmas colors and the choice of motifs bring some wintery flair to your designs. The 20 icons are available in EPS and AI and can be used for free in personal and commercial projects as long as you credit the designer.
Also designed by the folks at Freepik, this cheerful set59 consists of 16 AI and EPS icons with a unique nostalgic ’50s charm. You can use them for free in both personal and commercial projects, but please remember to credit the designer.
Christmas and snow go together like bread and butter. To add a bit of those snow flurries to your projects, Teela Cunningham has hand-drawn a set of snowflakes62 and turned them into vector graphics. The download includes AI and EPS files for private use only.
James Oconnell’s Ho Ho Ho icon set65 features ten Christmasy line art icons with a nifty twist; some lines are dotted while others are highlighted with an accent color. The centerpiece of the set is a squiggly “Ho Ho Ho” framed by snowflakes. The icons are available in AI format and you’re free to use them as you please.
Another set that pairs minimalistic line art with some lovely details comes from Cvijovic Zarko. These twelve icons68 cover classical Christmas motifs (a present, a snowman, reindeer, a candle, a sleigh, and more) and are available in EPS format.
In order to help kick-start the celebrations, here is a fantastic free Christmas and Winter-themed icon set to use in your own seasonal designs. With 27 color outlines, this New Year Celebration71 icon set has everything one could ask for. This will help you quickly design a holiday-themed UI, website, theme, or presentation.
A versatile three-in-one set74 that can be used in both personal and commercial projects comes from the folks at IconShock. It includes 40 icons with everything you’ll need to add some holiday flair to your projects — think presents, candles, stockings, a snowman, and much more. The vectors come in three styles (line, flat and line with colors) and are 100% editable.
Last but not least, to give the season of reindeer and Christmas elves a bit of a geeky glam, Tatiana Lapina designed 54 geeky Christmas vector graphics77. Among them, you’ll discover characters from Star Wars and famous computer games, geeky tech stuff, even a delivery drone to deliver the presents. The illustrations come in SVG, AI, EPS, PDF, and PNG formats and are free to use in personal and commercial projects.
Have you designed a free holiday icon set yourself? Is there perhaps that one set you keep using year after year? We’d love to hear about your favorites in the comments below!
Have you ever wondered what it takes to create a SpriteKit game from beginning to beta? Does developing a physics-based game seem daunting? Game-making has never been easier on iOS since the introduction of SpriteKit1.
In this three-part series, we will explore the basics of SpriteKit. We will touch on SKPhysics, collisions, texture management, interactions, sound effects, music, buttons and SKScenes. What might seem difficult is actually pretty easy to grasp. Stick with us while we make RainCat.
The game we will build has a simple premise: We want to feed a hungry cat, but it is outside in the rain. Ironically, RainCat does not like the rain all that much, and it gets sad when it’s wet. To fix this, we must hold an umbrella above the cat so that it can eat without getting rained on. To get a taste of what we will be creating, check out the completed project3. It has some more bells and whistles than what we will cover here, but you can look at those additions later on GitHub. The aim of this series is to get a good understanding of what goes into making a simple game. You can check in with us later on and use the code as a reference for future projects. I will keep updating the code base with interesting additions and refactoring.
We will do the following in this article:
check out the initial code for the RainCat game;
add a floor;
add raindrops;
prepare the initial physics;
add in the umbrella object to keep our cat dry from the rain;
begin collision detection with categoryBitMask and contactTestBitMask;
create a world boundary to remove nodes that fall off screen.
You will need to follow along with a few things. To make it easier to start, I’ve provided a base project. This base project removes all of the boilerplate code that Xcode 8 provides when creating a new SpriteKit project.
Get something to test on! In this case, it should be an iPad, which will remove some of the complexity of developing for multiple screen sizes. The simulator is functional, but it will always lag and run at a lower frame rate than a proper iOS device.
I’ve given you a head start by creating the project for the RainCat game and completing some initial steps. Open up the Xcode project. It will look fairly barebones at the moment. Here is an overview of what has happened up to this point: We’ve created a project, targeted iOS 10, set the devices to iPad, and set the orientation to landscape only. We can get away with targeting previous versions of iOS, back to version 8 with Swift 3, if we need to test on an older device. Also, a best practice is to support at least one version of iOS older than the current version. Just note that this tutorial targets iOS 10, and issues may arise if you target a previous version.
Side note on the usage of Swift 3 for this game: The iOS development community has been eagerly anticipating the release of Swift 3, which brings with it many changes in coding styles and improvements across the board. As new iOS versions are quickly and widely adopted by Apple’s consumer base, we decided it would be best to present the lessons in this article according to this latest release of Swift.
In GameViewController.swift, which is a standard UIViewController5, we reworked how we load the initial SKScene6 named GameScene.swift. Before this change, the code would load the GameScene class through a SpriteKit scene editor (SKS) file. For this tutorial, we will load the scene directly, instead of inflating it using the SKS file. If you wish to learn more about the SKS file, Ray Wenderlich has a great example7.
Before we can start coding, we need to get the assets for the project. Today we will have an umbrella sprite, along with raindrops. You will find the textures on GitHub8. Add them to your Assets.xcassets folder in the left pane of Xcode. Once you click on the Assets.xcassets file, you will be greeted with a white screen with a placeholder for the AppIcon. Select all of the files in a Finder window, and drag them below the AppIcon placeholder. If that is done correctly, your “Assets” file will look like this:
Now that we have a lot of the initial configuration out of the way, we can get started making the game.
The first thing we need is a floor, since we need a surface for the cat to walk and feed on. Because the floor and the background will be extremely simple, we can handle those sprites with a custom background node. Under the “Sprites” group in the left pane of Xcode, create a new Swift file named BackgroundNode.swift, and insert the following code:
import SpriteKit public class BackgroundNode : SKNode { public func setup(size : CGSize) { let yPos : CGFloat = size.height * 0.10 let startPoint = CGPoint(x: 0, y: yPos) let endPoint = CGPoint(x: size.width, y: yPos) physicsBody = SKPhysicsBody(edgeFrom: startPoint, to: endPoint) physicsBody?.restitution = 0.3 } }
The code above imports our SpriteKit framework. This is Apple’s library for developing games. We will be using this in pretty much every file we create from now on. This object that we are creating is an SKNode10. We will be using it as a container for our background. Currently, we just add an SKPhysicsBody11 to it when we call the setup(size:) function. The physics body will tell our scene that we want this defined area, currently a line, to interact with other physics bodies, as well as with the physics world12. We also snuck in a change to restitution. This property determines how bouncy the floor will be. To have it show up for us to use, we need to add it to GameScene. Move to the GameScene.swift file, and near the top of the file, underneath our group of TimeInterval variables, we can add this:
private let backgroundNode = BackgroundNode()
Then, inside the sceneDidLoad() function, we can set up and add the background to the scene with the following lines:
Now, if we run the app, we will be greeted with this game scene:
If you don’t see this line, then something went wrong when you added the node to the scene, or else the scene is not showing the physics bodies. To turn these options on and off, go to GameViewController.swift and modify these values:
Make sure that showsPhysics is set to true for now. This will help us to debug our physics bodies. Right now, this isn’t anything special to look at, but this will act as our floor for our raindrops to bounce off of, as well as the boundary within which the cat will walk back and forth.
Next, let’s add some raindrops.
If we think before we just start adding them to the scene, we’ll see that we’ll want a reusable function to add one raindrop to the scene at a time. The raindrop will be made up of an SKSpriteNode and another physics body. An SKSpriteNode can be initialized by an image or a texture. Knowing this, and also knowing that we will likely spawn a lot of raindrops, we need to do some recycling. With this in mind, we can recycle the texture so that we aren’t creating a new texture every time we create a raindrop.
At the top of the GameScene.swift file, above where we initialized backgroundNode, we can add the following line to the file:
let raindropTexture = SKTexture(imageNamed: "rain_drop")
We can now reuse this texture every time we create a raindrop, so that we aren’t wasting memory by creating a new one every time we want a raindrop.
Now, add in the following function near the bottom of GameScene.swift, so that we can constantly create raindrops:
This function, when called, will create a raindrop using the raindropTexture that we just initialized. Then, we’ll create an SKPhysicsBody from the shape of the texture, position the raindrop node at the center of the scene and, finally, add it to the scene. Because we added an SKPhysicsBody to the raindrop, it will be automatically affected by the default gravity and fall to the floor. To test things out, we can call this function in touchesBegan(_ touches:, with event:), and we will see this:
Now, as long as we keep tapping the screen, more raindrops will appear. This is for testing purposes only; later on, we will want to control the umbrella, not the rate of rainfall. Now that we’ve had our fun, we should remove the line that we added to touchesBegan(_ touches:, with event:) and tie in the rainfall to our update loop. We have a function named update(_ currentTime:), and this is where we will want to spawn our raindrops. Some boilerplate code is here already; currently, we are measuring our delta time, and we will use this to update some of our other sprites later on. Near the bottom of that function, before we update our self.lastUpdateTime variable, we will add the following code:
// Update the spawn timer currentRainDropSpawnTime += dt if currentRainDropSpawnTime > rainDropSpawnRate { currentRainDropSpawnTime = 0 spawnRaindrop() }
This will spawn a raindrop every time the accumulated delta time is greater than rainDropSpawnRate. Currently, the rainDropSpawnRate is 0.5 seconds; so, every half a second, a new raindrop will be created and fall to the floor. Test and run the app. It will act exactly as it did before, but instead of our having to touch the screen, a new raindrop will be created every half a second.
But this is not good enough. We don’t want one location from which to release the raindrops, and we certainly don’t want it to fall from the center of the screen. We can update the spawnRaindrop() function to position each new drop at a random x location at the top of the screen.
let xPosition = CGFloat(arc4random()).truncatingRemainder(dividingBy: size.width) let yPosition = size.height + raindrop.size.height raindrop.position = CGPoint(x: xPosition, y: yPosition)
After creating the raindrop, we randomize the x position on screen with arc4Random(), and we make sure it is on screen with our truncatingRemainder method. Run the app, and you should see the following:
We can play with the spawn rate, and we can spawn raindrops faster or slower depending on what value we enter. Update rainDropSpawnRate to be 0, and you will see many pretty raindrops. If you do this, you will notice that we have a big problem now. We are currently spawning unlimited objects and never getting rid of them. We will eventually be crawling at four frames per second and, soon after that, we’ll be out of memory.
Right now, there are only two types of collision. We have one collision between raindrops and one between raindrops and the floor. We need to detect when the raindrops hit something, so that we can tell it to be removed. We will add in another physics body that will act as the world frame. Anything that touches this frame will be deleted, and our memory will thank us for recycling. We need some way to tell the physics bodies apart. Luckily, SKPhysicsBody has a field named categoryBitMask. This will help us to differentiate between the items that have come into contact with each other.
To accomplish this, we should create another Swift file named Constants.swift. Create the file under the “Support” group in the left pane of Xcode. The “Constants” file enables us to hardcode values that will be used in many places across the app, all in one place. We won’t need many of these types of variables, but keeping them in one location is a good practice, so that we don’t have to search everywhere for these variables. After you create the file, add the following code to it:
let WorldCategory : UInt32 = 0x1
The code above uses a shift operator16 to set a unique value for each of the categoryBitMasks17 in our physics bodies. 0x1 is the hex value of 1, and 0x1 is the value of 2. 0x1 equals 4, and each value after that is doubled. Now that our unique categories are set up, navigate to our BackgroundNode.swift file, where we can update the physics body to the new FloorCategory. Then, we need to tell the floor physics body what we want to touch it. To do this, update the floor’s contactTestBitMask to contain the RainDropCategory. This way, when we have everything hooked up in our GameScene.swift, we will get callbacks when the two touch each other. BackgroundNode should now look like this:
import SpriteKit public class BackgroundNode : SKNode { public func setup(size : CGSize) { let yPos : CGFloat = size.height * 0.10 let startPoint = CGPoint(x: 0, y: yPos) let endPoint = CGPoint(x: size.width, y: yPos) physicsBody = SKPhysicsBody(edgeFrom: startPoint, to: endPoint) physicsBody?.restitution = 0.3 physicsBody?.categoryBitMask = FloorCategory physicsBody?.contactTestBitMask = RainDropCategory } }
The next step is to update the raindrops to the correct category, as well as update what it should come into contact with. Going back to GameScene.swift, in spawnRaindrop() we can add the following code after we initialize the raindrop’s physics body:
Notice that we’ve added in the WorldCategory here, too. Because we are working with a bitmask18, we can add in any category here that we want with bitwise operations19. In this instance for raindrop, we want to listen for contact when the raindrop hits either the FloorCategory or WorldCategory. Now, in our sceneDidLoad() function, we can finally add in our world frame:
In the code above, we’ve create a frame that is the same as the scenes, but we’ve increased the size so that it extends 100 points on either side. This way, we will have a buffer so that items aren’t deleted on screen. Note that we’ve used edgeLoopFrom, which creates an empty rectangle that allows for collisions at the edge of the frame.
Now that we have everything in place for detection, we need to start listening to it. Update the game scene to inherit from SKPhysicsContactDelegate. Near the top of the file, find this line:
class GameScene: SKScene {
And change it to this:
class GameScene: SKScene, SKPhysicsContactDelegate {
We now need to tell our scene’s physicsWorld20 that we want to listen for collisions. Add in the following line in sceneDidLoad(), below where we set up the world frame:
self.physicsWorld.contactDelegate = self
Then, we need to implement one of the SKPhysicsContactDelegate functions, didBegin(_ contact:). This will be called every time there is a collision that matches any of the contactTestBitMasks that we set up earlier. Add this code to the bottom of GameScene.swift:
Now, when a raindrop collides with the edge of any object, we’ll remove the collision bitmask of the raindrop. This prevents the raindrop from colliding with anything after the initial impact, which finally puts an end to our Tetris-like nightmare!
If there is a problem and the raindrops are not acting like in the GIF above, double-check that every categoryBitMask and contactTestBitMasks is set up correctly. Also, note that the nodes count in the bottom-right corner of the scene will keep increasing. The raindrops are not piling up on the floor anymore, but they are not being removed from the game scene. We will continue running into memory issues if we don’t start culling.
In the didBegin(_ contact:) function, we need to add the delete behavior to cull the nodes. This function should be updated to the following:
Now, if we run our code, we will notice that the node counter will increase to about six nodes and will remain at that count. If this is true, then we are successfully culling off-screen nodes!
The background node has been very simple until now. It is just an SKPhysicsBody, which is one line. We need to upgrade it to make the app look a lot nicer. Initially, we would have used an SKSpriteNode, but that would have been a huge texture for such a simple background. Because the background will consist of exactly two colors, we can create two SKShapeNodes to act as the sky and the ground.
Navigate to BackgroundNode.swift and add the following code in the setup(size) function, below where we initialized the SKPhysicsBody.
In the code above, we’ve created two SKShapeNodes that are basic rectangles. But a new problem arises from zPosition. Note in the code above that skyNode’s zPosition is 0, and the ground’s is 1. This way, the ground will always render in front of the sky. If you run the app now, you will see the rain draw in front of the sky but behind the ground. This is not the behavior we want. If we move back to GameScene.swift, we can update the spawnRaindrop() function and set the zPosition of the raindrops to render in front of the ground. In the spawnRaindrop() function, below where we set the spawn position, add the following line:
raindrop.zPosition = 2
Run the code again, and the background should be drawn correctly.
Now that the rain is falling the way we want and the background is set up nicely, we can start adding some interaction. Create another file under the “Sprites” group, named UmbrellaSprite.swift. Add the following code for the initial version of the umbrella.
import SpriteKit public class UmbrellaSprite : SKSpriteNode { public static func newInstance() -> UmbrellaSprite { let umbrella = UmbrellaSprite(imageNamed: "umbrella") return umbrella } }
The umbrella will be a pretty basic object. Currently, we have a static function to create a new sprite node, but we will soon add a custom physics body to it. For the physics body, we could use the function init(texture: size:), as we did with the raindrop, to create a physics body from the texture itself. This would work just fine, but then we would have a physics body that wraps around the handle of the umbrella. If we have a body around the handle, the cat would get hung up on the umbrella, which would not make for a fun game. Instead, we will add a SKPhysicsBody from the CGPath that we created in the static newInstance() function. Add the code below in UmbrellaSprite.swift, before we return the umbrella sprite’s newInstance() function.
We are creating a custom path for the umbrella’s SKPhysicsBody for two reasons. First, as mentioned, we only want the top part of the umbrella to have any collision. The second reason is so that we can be a little forgiving with the umbrella’s collision size.
The easy way to create a CGPath is to first create a UIBezierPath and append lines and points to create our basic shape. In the code above, we’ve created this UIBezierPath and moved the start point to the center of the sprite. The umbrellaSprite’s center point is 0,0 because our anchorPoint23 of the object is 0.5,0.5. Then, we add a line to the far-left side of the sprite and extend the line 30 points past the left edge.
Side note on usage of the word “point” in this context: A “point,” not to be confused with CGPoint or our anchorPoint, is a unit of measurement. A point may be 1 pixel on a non-Retina device, 2 pixels on a Retina device, and more depending on the pixel density of the device. Learn more about pixels and points on Fluid’s blog24.
Next, go to the top-center point of the sprite for the top edge, followed by the far-right side, and extend them the same 30 points out. We’re extending the edge of the physics body past the texture to give us more room to block raindrops, while maintaining the look of the sprite. When we add the polygon to SKPhysicsBody, it will close the path for us and give us a complete triangle. Then, set the umbrella’s physics to not be dynamic, so that it won’t be affected by gravity. The physics body that we drew will look like this:
Now make your way over to GameScene.swift to initialize the umbrella object and add it to the scene. At the top of the file and below our other class variables, add in this line:
private let umbrellaNode = UmbrellaSprite.newInstance()
Then, in sceneDidLoad(), beneath where we added backgroundNode to the scene, insert the following lines to add the umbrella to the center of the screen:
We will update the umbrella to respond to touch. In GameScene.swift, look at the empty functions touchesBegan(_ touches:, with event:) and touchesMoved(_ touches:, with event:). This is where we will tell the umbrella where we’ve interacted with the game. If we set the position of the umbrella node in both of these functions based on one of the current touches, it will snap into place and teleport from one side of the screen to the other.
Another approach would be to set a destination in the UmbrellaSprite object, and when update(dt:) is called, we can move toward that location.
Yet a third approach would be to set SKActions to move the UmbrellaSprite on touchesBegan(_ touches:, with event:) or touchesMoved(_ touches:, with event:), but I would not recommend this. This would cause us to create and destroy these SKActions frequently and likely would not be performant.
We will choose the second option. Update the code in UmbrellaSprite to look like this:
import SpriteKit public class UmbrellaSprite : SKSpriteNode { private var destination : CGPoint! private let easing : CGFloat = 0.1 public static func newInstance() -> UmbrellaSprite { let umbrella = UmbrellaSprite(imageNamed: "umbrella") let path = UIBezierPath() path.move(to: CGPoint()) path.addLine(to: CGPoint(x: -umbrella.size.width / 2 - 30, y: 0)) path.addLine(to: CGPoint(x: 0, y: umbrella.size.height / 2)) path.addLine(to: CGPoint(x: umbrella.size.width / 2 + 30, y: 0)) umbrella.physicsBody = SKPhysicsBody(polygonFrom: path.cgPath) umbrella.physicsBody?.isDynamic = false umbrella.physicsBody?.restitution = 0.9 return umbrella } public func updatePosition(point : CGPoint) { position = point destination = point } public func setDestination(destination : CGPoint) { self.destination = destination } public func update(deltaTime : TimeInterval) { let distance = sqrt(pow((destination.x - position.x), 2) + pow((destination.y - position.y), 2)) if(distance > 1) { let directionX = (destination.x - position.x) let directionY = (destination.y - position.y) position.x += directionX * easing position.y += directionY * easing } else { position = destination; } } }
A few things are happening here. The newInstance() function has been left untouched, but we’ve added two variables above it. We’ve added a destination variable (the point that we want to be moving towards); we’ve added a setDestination(destination:) function, to where we will ease the umbrella sprite; and we’ve added an updatePosition(point:) function.
The updatePosition(point:) will act exactly as though the position property was being updated directly before we made this update. Now we can update the position and the destination at the same time. This way, the umbrellaSprite will be positioned at this point and will stay where it is, because it will already be at its destination, instead of moving towards it immediately after setup.
The setDestination(destination:) function will only update the destination property; we will perform our calculations off of this property later. Finally, we added update(dt:) to compute how far we need to travel towards the destination point from our current position. We computed the distance between the two points, and if it is greater than one point, we compute how far we want to travel using the easing function. The easing function just finds the direction that the umbrella needs to travel in, and then moves the umbrella’s position 10% of the distance to the destination for each axis. This way, we won’t snap to the new location, but rather will move faster if we are further from the point, and slow down as the umbrella approaches its destination. If it is less than or equal to 1 pixel, then we will just jump to the final position. We do this because the easing function will approach the destination very slowly. Instead of constantly updating, computing and moving the umbrella an extremely short distance, we just set the position and forget about it.
Moving back to GameScene.swift, we should update our touchesBegan(_ touches: with event:) and touchesMoved(_ touches: with event:) functions to the following:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { let touchPoint = touches.first?.location(in: self) if let point = touchPoint { umbrellaNode.setDestination(destination: point) } } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { let touchPoint = touches.first?.location(in: self) if let point = touchPoint { umbrellaNode.setDestination(destination: point) } }
Now our umbrella will respond to touch. In each function, we check to see whether the touch is valid. If it is, then we tell the umbrella to update its destination to the touch’s location. Now we need to modify the line in sceneDidLoad():
Thus, our initial position and destination will be set correctly. When we start the scene, we won’t see the umbrella move without us interacting with the app. Lastly, we need to tell the umbrella to update in our own update(currentTime:) function.
Add the following code near the end of our update(currentTime:) function:
umbrellaNode.update(deltaTime: dt)
When we run the code, we should be able to tap and drag around the screen, and the umbrella will follow our touching and dragging.
So, that’s lesson one! We’ve covered a ton of concepts today, jumping into the code base to get our feet wet, and then adding in a container node to hold our background and ground SKPhysicsBody. We also worked on spawning our raindrops at a constant interval, and had some interaction with the umbrella sprite. The source code for today is available on GitHub27.
How did you do? Does your code look almost exactly like mine? What changed? Did you update the code for the better? Was I not clear in explaining what to do? Let me know in the comments below.
Thank you for making it this far. Stay tuned for lesson two of RainCat!
We have great new technology available to enhance our websites. But while theoretical articles explain well what the technologies do, we often struggle to find real use cases or details on how things worked out in actual projects.
This week I stumbled across a couple of great posts that share exactly these precious real-life insights: stories about HTTP/2 implementation, experiences from using the Cascade of CSS in large-scale projects, and insights into employing Service Worker and BackgroundSync to build solid forms.
Michael Scharnagl explains how you can enhance a basic form9 (i.e. a login or comment form) with custom validation, AJAX requests, auto-expansion of a textarea, and, finally, Service Worker and BackgroundSync to store input when a connection is unstable.
I don’t want to write too much about this weird Black Friday thing that causes millions of people to buy stuff they’ll never use or need, but Jason Koebler has the best Black Friday deal ever: repair a gadget you already own13 instead of buying new stuff.
Bill Sourour’s article “The Code I’m Still Ashamed Of16” points out an important aspect of our jobs as developers: responsibility. An incredibly important story.
The resurgence of hand lettering, calligraphy, signage, penmanship, or really anything that is graphic and handmade is increasingly difficult to ignore. Along with letters drawn in any of the categories just mentioned, drawing, sketching, sketchnoting1, and any hybrid style (combinations of the above) have also been gaining attention among designers, illustrators, and other professionals. A quick look around social media or simply googling lettering2 will quickly show impressive and notable work.
Last year I deliberately started practicing brush lettering, meaning I had a dedicated time to practice exercises, write out words and practice letterforms. In the process, I learned a few things I would like to share. These tips are not just about how to write messages or repeating letters over and over. Instead, I have become familiar with methods and approaches that have helped me in my journey to improve my lettering work.
This is the first part of two articles which aim to provide you with a good foundation of why lettering is more than just drawing pretty letters and explains the principles behind the practice. I believe that knowing why we do things is important because it helps us create a mindful practice. With that in mind, we will start with a little background, context, and supplies. In the second part of this article, we will move on towards practical advice, how-to videos, and freebies. If you would like to see my work and how I have progressed, please visit my Instagram page3 or go to my blog4.
As I mentioned before, hand lettering, calligraphy, signage, and penmanship, have experienced a resurgence among designers, as well as non-designers. These graphic forms of expression have one common element: they are activities performed manually through observation and, often, repetition. The acts of drawing, doodling, illustrating, and by extension, drawing or illustrating letters, and writing are considered extremely beneficial to increase memory, retention, and to improve eye-hand coordination.
Recent studies (one referenced in Science Daily8 and the others referenced in The New York Times9) claim there is evidence that the act of drawing itself helps to boost memory and learning. These new findings are important as they show the act of drawing itself is what matters — not what is being drawn. Thus, one could infer that drawing letters would contribute to the benefits gathered by simply engaging in the act of creating them. It has also been noted that taking notes with the keyboard is not as beneficial in the learning process and retention as taking notes by hand.
“Handwriting seems to have lost some of its attraction over the last years. Nobody writes beautiful handwritten letters, and uses digital means of communication with smileys, abbreviations and standard lettering instead. And that’s a pity. Since handwriting is unique, it has a tremendous expressive power a standard lettering isn’t able to achieve.”
This article published in The Washington Post titled “Cursive Handwriting Is Disappearing From Public Schools12” by T. Ress Shapiro in 2013 echoes Friedman’s sentiment and explains how handwriting was disappearing from the school curriculum in the United States mainly due to two factors:
Technology has been slowly replacing handwriting.
Common Core Standards do not require students to learn cursive handwriting. It leaves it as a decision to be made by local school officials.
In contrast, the Spencerian Penmanship Theory book15, a book that focuses on teaching both the theory and practical lessons of handwriting, states in its Introductory Remarks:
“Writing is a secondary power of speech, and they who cannot write are in part dumb.”
Because there is still much debate going on regarding teaching or not teaching handwriting, at least in the United States, we may have young designers who have not learned specifics of writing by hand. In turn, these designers who may become interested in lettering as an extension of typography, will find themselves looking for ways to learn to do lettering. However, even those of us who learned handwriting and also find typography intriguing, find ourselves fascinated by lettering. Thus, I decided to learn and practice.
Engaging in deliberate practice was not new to me. Back in January 2010, I committed to do something creative every day inspired by Jad Limaco16‘s article Design Something Every Day17. Because Instagram had not been released yet — it was released in October 201018 — I documented all the work in a blog19 and on Twitter20.
A little over a year ago, I started practicing lettering and sometimes penmanship deliberately each day. If you ask me why, I could offer several reasons. Among those reasons: drawing letters has been something I practiced since I was young. Another: I am fascinated (ahem, “obsessed” would be the best term) with typographic forms and letters. I was one of those kids who could write their name on long stretches of notebook paper in three or four different styles. One of my hobbies was to try to imitate my father’s signature because it was not only beautiful but also complicated.
Though my affinity for letterforms has been a long-standing affair, as soon as I started to post my work on Instagram, it was clear that I did not have a full grasp of the basic concepts as I once thought. That said, Instagram is very encouraging and, while it may sound silly and childish, the likes are a good encouragement to keep going.
As they say, practice makes progress. Here are examples of my lettering in chronological order of my progress:
As we discussed, teaching cursive has been in decline in the education system. I believe that it is precisely due to the lack of exposure in the formative school years, that lettering, calligraphy, and penmanship in general has grown among designers. For instance, on any social media outlet: Twitter26, Pinterest27, Flickr28, Facebook29, and Instagram30, one can find evidence of its popularity. Instagram has become the preferred online space to share work for calligraphers and letterers. Using any of the terms on the list below would prompt various examples.
Hashtags (each is a link to a page featuring the results of the hashtag search)
Rather than delving into a historical and background account of brush lettering and its relationship to its first cousins, calligraphy, penmanship, and signage, I thought we would discuss the tools, paper, the lessons I have learned, and my favorite letterers and calligraphers. However, before we do that, let’s go over some terms and parameters so that we are all on the same page.
Because the terms “calligraphy”, “lettering”, and “typography” are often used interchangeably, I think it would be beneficial to define each of these. For the most part, people are not obsessively trying to correct each other. Nonetheless, understanding the differences and similarities is very helpful.
“The word ‘calligraphy’ comes from the Greek words kallos, ‘beauty’, and graph, ‘writing’.”
Thus, calligraphy is an art, but it is the art of writing out the words. It has a close relationship to penmanship. The dictionary defines penmanship as the art or skill of writing by hand; a person’s handwriting. Calligraphy, on the other hand, is the art of producing decorative handwriting or lettering with a pen or brush. There are rules to follow and alphabets, called hands in calligraphy, to learn: Neuland49, Roman Capitals50, Italic51, Foundational52, Uncial53, Carolingian54, Gothic55, and Versals56. (Each term is linked to a Google Image search for your benefit). However, here are examples of some of the calligraphic hands:
When you practice one of these alphabets, or a calligraphic hand, it is crucial that you do them correctly. There are many technical nuances in the construction of any calligraphic alphabet to be mindful of. To me, calligraphy is the equivalent of classical ballet. Learn it well and you are versed in the principles and theory that open the key to more expressive styles. Not every letterer is a calligrapher but every calligrapher can be and is a letterer.
Mary Kate McDevitt69, in her book Hand Lettering Ledger70, defines lettering as the art of drawing letters. Lettering is the customization of letters. In other words, lettering allows the designer and artist to create a style that was not there before or to embellish an existing letterform beyond its original form. You can see many examples of lettering by doing an image search on Google or just by clicking here71.
In the book Typographic Design: Form and Communication75, Rob Carter76 states that typography and writing are magic because they create a “record of the spoken language.” Before the computer, letters (typography) were set to specific types or molds and arranged in the printing press. The letters were carved out of wood or metal and moved to create words, sentences, paragraphs, and pages. These letters needed to be functional, meaning that, because the letters belonged to a family, they needed to share many elements to look like they belong.
In this aspect, typeface design is similar to calligraphy. The letters in each font as in calligraphy had to look alike. Robert Bringhurst, in his book The Elements of Typographic Style80, states that letters need to be “set with affection, intelligence, knowledge, and skill.”
Now that we have these terms defined, let’s focus on brush lettering. You may be wondering why I make a point to differentiate between lettering (drawing letters) and brush lettering as if they are not the same. Well, they are not the same. In my case, specifically, I don’t tend to create illustrative letters. Perhaps I am a little too impatient for it. I admire and respect those who create illustrative lettering, like the work of Jason Roeder8173.
As its name says, brush lettering is lettering done with a brush. Though it has been around for a long time, it is still often seen as a very modern style of writing based on calligraphy. The brush width is not as important as the angle you hold it. You can use a flat, angled brush, or a pointed brush. Though, something to keep in mind: the size or width of the brush determines the size of the lettering.
The best pen to use is the one you have. Believe this. The specific pen helps, but it is your ability to use the pen correctly and your understanding of the basic principles of forming a letter that ultimately will make you a good letterer (brush or otherwise). That said, there are many pens out there. Some are inexpensive while others are very expensive. The best advice I can give you is to try as many pens as you are able. One thing to remember is, the larger the size of the tip of the brush pen, the larger the letters will be. Below are some of the best pens to get started with.
Crayola9385 markers broad tips are a great way to get used to changing the pressure of the marker on the page. Also, they are very inexpensive. (Large preview86)
At the beginning, it was easier for me to use Crayola markers than other brush pens. Why would I use these markers if I wanted to practice brush lettering? Here are a few reasons:
Crayola broad tip markers are great to use when you are just starting out because they can take a lot of abuse. Their tip is broad and sturdy. Thus, I did not have to be concerned with the tip. Brush pens are delicate and the tip may fray after a lot of use. Plus, while learning, replacing the Crayola markers is a lot cheaper than replacing a Tombow87 brush pen. I also used the Crayola broad tip markers to help me get the feel of the most basic principle: pressure when drawing a down stroke and very light pressure when going up. More on the pressure later. If you are like me, get a set of Crayola markers and be happy practicing.
Crayola broad tip markers have become popular to the point that there is now a term mixing calligraphy and Crayola. The term is Crayligraphy88. The term was coined by Colin Tierny89 and he defines it as:
“The art of stylistically writing with a Crayola marker.”
Here is an example of what lettering looks like with Crayola markers.
Once I began getting used to the techniques, I decided to try these brush pens. They are very inexpensive and last for a while. The tip is very sturdy, so it is hard to fray, but one has to be careful when and how to apply the pressure. The brush tip is very flexible; thus it may be hard to make the transition between the sturdiness of the Crayola broad tip markers and their brush pens. However, the good news is that replacing them will not be painful as they are inexpensive. I find the color in them to be very rich, vibrant, and it runs very well.
These are the pens I have been using in all the videos for this article (you will be able to see them in part 2). These are a great next step from the Crayola markers or pen brushes. Tombow Fudensouke pens have a tip that feels like a brush but is actually plastic. The soft pen tip is very flexible, which allows you to experiment with the pressure and bending the tip. Since the tip is not a brush and there are not brush hairs to speak of, the tips will not fray. The hard tip is very firm and it really helps you to be conscious of how much pressure you need to apply on each stroke to get the thick and thins on paper. These are more expensive than the Crayola markers but, you can get them in sets. Plus, they last a long time.
Many brush letterers consider these pens the top of the line. Their tip is large compared to the Fudenosuke pens above, so these brush pens are more appropriate when lettering at a larger scale. They are very smooth and work best when used with marker paper or soft paper. However, I like texture, and sometimes I use them on paper with some teeth in it. This is where you need to be careful, because that can fray the tip of the brush.
You can buy these pens individually or in sets at several art supply stores.
These are my favorite brush pens. I even make excuses to use them. The brush tip is very smooth and glides on the paper even when the paper has some texture. Here are some advantages to these brush pens:
The body or barrel can be filled with water, ink, or simply liquid watercolors. If filling with ink or liquid watercolor, I found that using a dropper works best to fill the barrel. The liquid tends to create a bubble that you will need to burst, or you will be a mess of ink or color! I use a dropper and a paper towel to burst the bubble on the opening. Though this process can be slow, the good news is that the ink or liquid watercolor tends to last a long time. When filled with water, the brush pen will likely be used with watercolors. Thus, it runs out of water a little faster. However, if I want to take my supplies with me when traveling (and I do take my supplies with me everywhere), these are great if using watercolors as there is no need to change the water regularly.
These brush pens can be bought separately or in a set of three: fine, medium, and large. I rarely use the medium and large, but it is more cost effective to buy them as a set. I find that the fine tip allows me to do different sizes in my lettering depending on how I hold the brush and how much I bend it.
These brush pens are also one of my favorites. The tip is plastic and flexible like the Fudenosuke Soft above, but they come in sets of color and can be used for small to medium lettering. The color is very crisp, and it looks like watercolor depending on the paper you are using. You can also buy them in sets of six and twelve.
My best friend gave me these watercolor brush pens as Christmas gift and I must say they are divine. They have a very delicate tip so I don’t use them to practice with. I use them to do final pieces or nice lettering for someone. The color is beautiful, really beautiful, and the tip just glides on the paper. These brush pens are a must have if you want a piece with a more delicate and polished finish.
There are of course more brush pens available at different online stores. JetPens119, for instance, offers two sets of brush pens; an assortment of different pens to try. This deal may be a good option before you commit to a brand or if you just want to explore.
One thing to remember about the brush pens: the size of the tip determines the size of the letters you will write. Keep that in mind when choosing pens for your work. I know I keep reminding you of this, but it is important.
These brush pens are both firm and flexible. They allow me to create drastic thick and thin strokes, making for a beautiful contrast effect. They are small, thin, and light to hold. However, they do not last as long as other brands.
This is my newest pen, and I love it. I can write really small and delicately and the tip is firm but not so hard that it has no flexibility. The pen is sold in parts: the body and the fill and you can buy the refill in a variety of colors. See how small I can write with it in these images below.
There are of course more brush pens available. However, the list offered to you here is a good place to start. When I started, I had no idea which pens were best, and the Crayola markers were my favorite for a long time. Now, I switch it around depending on the type of work I am doing. If you feel like you need to have one pen to start with, I would recommend the Tombow Fudenosuke130 soft and hard tip brush pens.
Let’s now discuss the second most important supply: paper.
There are many brush letterers and brush calligraphers, and each one of them will have a list of which papers are best. What I am listing here are the ones I have used and like.
I started out practicing on laser paper. It is an inexpensive way to get started, and some of it is thick enough to hold the moisture. Lindsey Bugbee131 from The Postman’s Knock recommends using Georgia Pacific 20lb on one of her blog posts132. I have found that laser paper, both in 24lb and 32lb weights, is very nice and the brushes handle it very well. Please note that if you use your brush pens on laser paper, your pens will eventually fray. For practice purposes, laser paper is good but use the least expensive brushes on it. Also, remember that the cheapest writing tool you can practice with is a pencil.
If you plan to keep your practice sheets, it is best to use archival quality paper. For final pieces, it is best to find paper that is not only archival but also smooth. The smooth type of paper will be very gentle on your brush pen markers. Amanda Arneilla133 has a great post on her site about paper that you may want to check out!
Many brush letterers and brush calligraphers prefer Rhodia pads. I have a pad with a quad-grid, and I love its texture. It’s very smooth, and the brush pen glides on it. The only drawback is that it can be a tad expensive, or not as cost effective as others.
A few years ago, I took calligraphy classes and my teacher, Clint Voris, had me practice on a large (11″ x 14″) drawing paper pad. The brand he recommended was Strathmore135. The ink did not bleed through the paper, and I have found that some drawing papers, as long as they are smooth, can work well for brush pens. However, I would use the plastic and synthetic brush tips with this paper. As the Tombow markers are delicate, I would only use them on either the Rhodia pads or smooth laser paper.
This is my favorite paper as I do a lot of watercolor lettering and it comes in pads and large rolls. However, for some reason, the paper in the roll is lighter than in the pads. Regardless, it is very thick and holds a lot of moisture. Since this paper is intended for both dry and wet media, it works very well with the plastic and synthetic brushes due to its soft texture. The stroke you make with the brush will reflect that (see picture below). I don’t mind a little bit of texture; in fact, I find it gives the lettering a very rich feel. This pad is also great if you are experimenting with watercolor lettering or watercolor backgrounds.
These pads are not expensive and for practicing purposes, and I use both sides. The best news? If your practice turns out well, you may be able to sell it, as this is good paper!
As with the Canson and Strathmore, I don’t use the Tombow dual brush pens on this paper. Even the soft texture of this paper would fray the Tombow brushes a little. However, if you do like some texture, you can use the frayed brush tips to give you that feel of texture. Below are two example of lettering done on this paper.
Since I teach design at a university, I have access to lots and lots of discarded pieces of printmaking paper. You may find this paper strange because it has a lot of texture. That is true, but it also holds a lot of moisture. Sometimes I use my brush pens, such as the Crayola brush pens, on it, and I get great results. The texture of the paper makes it even better for my purposes. I love to see how the ink and the watercolor interact with it. See this image below.
Other Items You Will Need But Probably Have Around Link
I can’t stress how crucial it is to plan out your letters before marking the paper with ink. Once there is ink, well, it will not be erased. Plus, sketching out or planning where you want your letters will help you get better at understanding proportions: how large the letters should be in relation to the size of the sheet you are using. If you use soft pencils, you can practice creating thick and thin strokes in your letters. Any soft pencil will do but among some brush letterers the Palomino Blackwing Pencils143 are a favorite.
I will be honest and confess that I dislike drawing straight lines because, for some reason, they always end up in a slight diagonal. But, better some lines than none, right? As you get better, your sense for letter placement will improve, and you will rely less on the ruler. But please, at least use one to create a few guidelines to start. I will say that if you practice on a grid or lined paper, you will get used to writing on a line. After a while, even when no line exists, you may notice that you are able to write fairly straight.
There is no explanation needed except that make sure to use plastic erasers. They are usually white and are not abrasive on the paper. These erasers are so good that they fade dried watercolor.
We have discussed brush lettering, its definition, its context, and the supplies you will need to get started. In the second part of this article, we will discuss the practice itself. Plus, there will be some videos and freebies. Stay tuned!