Jump to Navigation

Around The Web

Use Case For Augmented Reality In Design

Smashing Magazine - 10 hours 44 min ago
Use Case For Augmented Reality In Design Use Case For Augmented Reality In Design Suzanne Scacca 2018-11-14T14:00:49+01:00 2018-11-14T13:45:20+00:00

Augmented reality has been on marketers’ minds for years now — and there’s a good reason for it. Augmented reality (or AR) is a technology that layers computer-generated images on top of the real world. With the pervasiveness of the mobile device around the globe, the majority of consumers have instant access to AR-friendly devices. All they need is a smartphone connected to the Internet, a high-resolution screen, and a camera viewfinder. It’s then up to you as a marketer or developer to create digital animations to superimpose on top of their world.

This reality-bending technology is consistently named as one of the hot development and design trends of the year. But how many businesses and marketers are actually making use of it?

As with other cutting-edge technologies, many have been reluctant to adopt AR into their digital marketing strategy.

Part of it is due to the upfront cost of using and implementing AR. There’s also the learning curve to think about when it comes to designing new kinds of interactions for users. Hesitation may also come from marketers and designers because they’re unsure of how to use this technology.

Augmented reality has some really interesting use cases that you should start exploring for your mobile app. The following post will provide you with examples of what’s being done in the AR space now and hopefully inspire your own efforts to bring this game-changing tech to your mobile app in the near future.

The Future Is Here: AR & VR Icon Set

Looking for an icon set that’ll take you on a journey through AR and VR technology? We’ve got your back. Check out the freebie →

Web forms are such an important part of the web, but we design them poorly all the time. The brand-new “Form Design Patterns” book is our new practical guide for people who design, prototype and build all sorts of forms for digital services, products and websites. The eBook is free for Smashing Members.

Check the table of contents ↬ Augmented Reality: A Game-Changer You Can’t Ignore

Unlike virtual reality, which requires users to purchase pricey headsets in order to be immersed in an altered experience, augmented reality is a more feasible option for developers and marketers. All your users need is a device with a camera that allows them to engage with the external world, instead of blocking it out entirely.

And that’s essentially the crux of why AR will be so important for mobile app companies.

This is a technology that enables mobile app users to view the world through your “filter.” You’re not asking them to get lost in another reality altogether. Instead, you want to merge their world with your own. And this is something websites have been unable to accomplish as most interactions are lacking in this level of interactivity.

Let’s take e-commerce websites, for example. Although e-commerce sales increase year after year, people still flock to brick-and-mortar stores in droves (especially for the holiday season). Why? Well, part of it has to do with the fact that they can get their hands on products, test things out and talk to people in real time as they ponder a purchase. Online, it’s a gamble.

As you can imagine, AR in a mobile app can change all that. Augmented reality allows for more meaningful engagements between your mobile app (and brand) and your user. That’s not all though. Augmented reality that connects to geolocation features could make users’ lives significantly easier and safer too. And there’s always the entertainment application of it.

If you’re struggling with retention rates for your app, developing a useful and interactive AR experience could be the key to winning more loyal users in the coming year.

Inspiring Examples Of Augmented Reality

To determine what kind of augmented reality makes the most sense for your website or app, look to examples of companies that have already adopted and succeeded in using this technology.

As Google suggests:

“Augmented reality will be a valuable addition to a lot of existing web pages. For example, it can help people learn on education sites and allow potential buyers to visualize objects in their home while shopping.”

But those aren’t the only applications of AR in mobile apps, which is why I think many mobile app developers and marketers have shied away from it thus far. There are some really interesting examples of this out there though, and I’d like to introduce you to them in the hopes it’ll inspire your own efforts in 2019 and beyond.

Social Media AR

For many of us, augmented reality is already part of our everyday lives, whether we’re the ones using it or we’re viewing content created by others using it. What am I talking about? Social media, of course.

There are three platforms, in particular, that make use of this technology right now.

Snapchat was the first:

Trying out a silly filter on Snapchat (Source: Snapchat) (Large preview)

Snapchat could have included a basic camera integration so that users could take and send photos and videos of themselves to others. But it’s taken it a step further with face mapping software that allows users to apply different “filters” to themselves. Unlike traditional filters which alter the gradients or saturation of a photo, however, these filters are often animated and move as the user moves.

Instagram is another social media platform that has adopted this tech:

Instagram filters go beyond making a face look cute. (Source: Instagram) (Large preview)

Instagram’s Stories allow users to apply augmented filters that “stick” to the face or screen. As with Snapchat, there are some filters that animate when users open their mouths, raise their eyebrows or make other movements with their faces.

One other social media channel that’s gotten into this — that isn’t really a social media platform at all — is Facebook’s Messenger service:

Users can have fun while sending photos or video chatting on Messenger. (Source: Messenger) (Large preview)

Seeing as how users have flocked to AR filters on Snapchat and Instagram, it makes sense that Facebook would want to get in on the game with its mobile property.

Use Case

Your mobile app doesn’t have to be a major social network in order to reap the benefits of image and video filters.

If your app provides a networking or communication component — in-app chat with other users, photo uploads to profiles and so on — you could easily adopt similar AR filters to make the experience more modern and memorable for your users.

Video Objects AR

It’s not just your users’ faces that can be mapped and altered through the use of augmented reality. Spaces can be mapped as well.

While I will go on to talk about pragmatic applications of space mapping and AR shortly, I do want to address another way in which it can be used.

Take a look at 3DBrush:

Adding 3D objects to video with 3DBrush. (Source: 3DBrush)

At first glance, it might appear to be just another mobile app that enables users to draw on their photos or videos. But what’s interesting about this is the 3D and “sticky” aspects of it. Users can draw shapes of all sizes, colors and complexities within a 3D space. Those elements then stick to the environment. No matter where the users’ cameras move, the objects hold in place.

LeoApp AR is another app that plays with space in a fun way:

LeoApp maps a flat surface for object placement. (Source: LeoApp AR) (Large preview)

As you can see here, I’m attempting to map this gorilla onto my desk, but any flat surface will do.

A gorilla dances on my desk, thanks to LeoApp AR. (Source: LeoApp AR)

I now have a dancing gorilla making moves all over my workspace. This isn’t the only kind of animation you can put into place and it’s not the only size either. There are other holographic animations that can be sized to fit your actual physical space. For example, if you wanted to chill out side-by-side with them or have them accompany you as you give a presentation.

Use Case

The examples I’ve presented above aren’t the full representation of what can be done with these mobile apps. While users could use these for social networking purposes (alongside other AR filters), I think an even better use of this would be to liven up professional video.

Video plays such a big part in marketing and will continue to do so in the future. It’s also something we can all readily do now with our smartphones; no special equipment is needed.

As such, I think that adding 3D messages or objects into a branded video might be a really cool use case for this technology. Rather than tailor your mobile app to consumers who are already enjoying the benefits of AR on social media, this could be marketed to businesses that want to shake things up for their brand.

Gaming AR

Thanks to all the hubbub surrounding Pokémon Go a few years back, gaming is one of the better known examples of augmented reality in mobile apps today.

My dog hides in the bushes from Pokemon. (Source: Pokémon Go) (Large preview)

The app is still alive and well and that may be because we’re not hearing as many stories about people becoming seriously injured (or even dying) from playing it anymore.

This is something that should be taken into close consideration before developing an AR mobile app. When you ask users to take part in augmented reality outside the safety of a confined space, there’s no way to control what they do afterwards. And that could do some serious damage to your brand if users get injured while playing or just generally wreak havoc out in the public forum (like all those PG users who were banned from restaurants).

This is probably why we see AR more used in games like AR Sports Basketball these days.

Users can map a basketball hoop onto any flat surface with AR Sports Basketball. (Source: AR Sports Basketball)

The app maps a flat surface — be it a smaller version on a desk or a larger version placed on your floor — and allows users to shoot hoops. It’s a great way to distract and entertain oneself or even challenge friends, family or colleagues to a game of HORSE.

Use Case

You could, of course, build an entire mobile app around an AR game as these two examples have shown.

You could also think of ways to gamify other mobile app experiences with AR. I imagine this could be used for something like a restaurant app. For example, a pizza restaurant wants to get more users to install the app and to order food from them. With a big sporting event like the Super Bowl coming up, a “Play” tab is added to the app, letting users throw pizzas down the field. It would certainly be a fun distraction while waiting for their real pizzas to arrive.

Bottom line: get creative with this. AR games aren’t just for gaming apps.

Home Improvement AR

As you’ve already seen, augmented reality enables us to map physical spaces and stick interactive objects to them. In the case of home improvement, this technology is being used to help consumers make purchasing decisions from the comfort of their home (or at their job or on their commute to work, etc.)

IKEA is one such brand that’s capitalized on this opportunity.

Place IKEA products around your home or office. (Source: IKEA) (Large preview)

To start, here is my attempt at shopping for a new desk for my workspace. I selected the product I was interested in and then I placed it into my office. Specifically, I put the accurately sized 3D desk projection in front of my current desk, so I could get a sense for how the two differ and how this new one would fit.

While product specifications online are all well and good, consumers still struggle with making purchases since they can’t truly envision how those products will (physically) fit into their lives. The IKEA Place app is aiming to change all of that.

Take a photo with the IKEA map and search related products. (Source: IKEA) (Large preview)

The IKEA app is also improving the shopping experience with the feature above.

Users open their camera and point it at any object they find in the real world. Maybe they were impressed by a bookshelf they saw at a hotel they stayed in or they really liked some patio chairs their friends had. All they have to do is snap a picture and let IKEA pair them with products that match the visual description.

IKEA pairs app users with relevant product results. (Source: IKEA) (Large preview)

As you can see, IKEA has given me a number of options not just for the chair I was interested in, but also a full table set.

Use Case

If you have or want to build a mobile app that sells products to B2C or B2B consumers and these products need to fit well into their physical environments, think about what a functionality like this would do for your mobile app sales. You could save time having to schedule on-site appointments or conduct lengthy phone calls whereby salespeople try to convince them that the products, equipment or furniture will fit. Instead, you let the consumers try it for themselves.

Self-Improvement AR

It’s not just the physical spaces of consumers that could use improvement. Your mobile app users want to better themselves as well. In the past, they’d either have to go somewhere in person to try on the new look or they’d have to gamble with an online purchase. Thanks to AR, that isn’t the case anymore.

L’Oreal has an app called Style My Hair:

Try out a new realistic hair color with the L’Oreal app. (Source: Style My Hair) (Large preview)

In the past, these hair color tryouts used to look really bad. You’d upload a photo of your face and the website would slap very fake-looking hair onto your head. It would give users an idea of how the color or style worked with their skin tone, eye shape and so on, but it wasn’t always spot-on which would make the experience quite unhelpful.

As you can see here, not only does this app replace my usually mousy-brown hair color with a cool new blond shade, but it stays with me as I turn my head around:

L’Oreal applies new hair color any which way users turn. (Source: Style My Hair) (Large preview)

Sephora is another beauty company that’s taking advantage of AR mapping technology.

Try on beauty products with the Sephora app. (Source: Sephora) (Large preview)

Here is an example of me feeling not so sure about the makeup palette I’ve chosen. But that’s the beauty of this app. Rather than force customers to buy a bunch of expensive makeup they think will look great or to try and figure out how to apply it on their own, this AR app does all the work.

Use Case

Anyone remember the movie The Craft? I totally felt like that using this app.

The Craft hair-changing clip definitely inspired this example. (Source: The Craft)

If your app sells self-improvement or beauty products, or simply advises users on next steps they should take, think about how AR could transform that experience. You want your users to be confident when making big changes — whether it be how they wear their makeup for date night or the next tattoo they put on their body. This could be what convinces them to take the leap.

Geo AR

Finally, I want to talk about how AR has and is about to transform users’ experiences in the real world.

Now, I’ve already mentioned Pokémon Go and how it utilizes the GPS of a users’ mobile device. This is what enables them to chase those little critters anywhere they go: restaurants, stores, local parks, on vacation, etc.

But what if we look outside the box a bit? Geo-related AR doesn’t just help users discover things in their physical surroundings. It could simply be used as a way to improve the experience of walking about in the real world.

Think about the last time you traveled to a foreign destination. You may have used a translation guidebook to look up phrases you didn’t know. You might have also asked your voice assistant to translate something for you. But think about how great it would be if you didn’t have to do all that work to understand what’s right in front of you. A road sign. A menu. A magazine article.

The Google Translate app is attempting to bridge this divide for us:

Google Translate uses the camera to find foreign text. (Source: Google Translate) (Large preview)

In this example, I’ve scanned an English phrase I wrote out: “Where is the bathroom?” Once I selected the language I wanted to translate from and to, as well as indicated which text I wanted to focus on, Google Translate attempted to provide a translation:

Google Translate provides a translation of photographed text. (Source: Google Translate) (Large preview)

It’s not 100% accurate — which may be due to my sloppy handwriting — but it would certainly get the job done for users who need a quick way to translate text on the go.

Use Case

There are other mobile apps that are beginning to make use of this geo-related AR.

For instance, there’s one called Find My Car that I took for a test spin. I don’t think the technology is fully ready yet as it couldn’t accurately “pin” my car’s location, but it’s heading in the right direction. In the future, I expect to see more directional apps — especially, Google and Apple Maps — use AR to improve directional awareness and guidance for users.

Wrapping Up

There are challenges in using AR, that’s for sure. The cost of developing AR is one. Finding the perfect application of AR that’s unique to your brand and truly improves the mobile app user experience is another. There’s also the fact it requires users to download a mobile app, so there’s a lot of work to be done to motivate them to do so.

Gimmicks just won’t work — especially if you expect users to download your app and make use of it (remember: retention rates aren’t just about downloads). You have to make the augmented reality feature something that’s worth engaging. The first place to start is with your data. As Jordan Thomson wrote:

“AR is a lot more dependent on customer activity than VR, which is far older technology and is perhaps most synonymous with gaming. Designers should make use of big data and analytics to understand their customers’ wants and needs.”

I’d also advise you to spend some time in the apps above. Get a sense for how the technology works and discover what makes it so appealing on a personal level. Compare it to your own mobile app’s goals and see if there’s a way to take AR from just being an idea you’re tossing around to a reality.

(ra, yk, il)
Categories: Around The Web

Sending Emails Asynchronously Through AWS SES

Smashing Magazine - Tue, 11/13/2018 - 8:30am
Sending Emails Asynchronously Through AWS SES Sending Emails Asynchronously Through AWS SES Leonardo Losoviz 2018-11-13T14:30:53+01:00 2018-11-14T13:45:20+00:00

Most applications send emails to communicate with their users. Transactional emails are those triggered by the user’s interaction with the application, such as when welcoming a new user after registering in the site, giving the user a link to reset the password, or attaching an invoice after the user does a purchase. All these previous cases will typically require sending only one email to the user. In some other cases though, the application needs to send many more emails, such as when a user posts new content on the site, and all her followers (which, in a platform like Twitter, may amount to millions of users) will receive a notification. In this latter situation, not architected properly, sending emails may become a bottleneck in the application.

That is what happened in my case. I have a site that may need to send 20 emails after some user-triggered actions (such as user notifications to all her followers). Initially, it relied on sending the emails through a popular cloud-based SMTP provider (such as SendGrid, Mandrill, Mailjet and Mailgun), however the response back to the user would take seconds. Evidently, connecting to the SMTP server to send those 20 emails was slowing the process down significantly.

After inspection, I found out the sources of the problem:

  1. Synchronous connection
    The application connects to the SMTP server and waits for an acknowledgment, synchronously, before continuing the execution of the process.
  2. High latency
    While my server is located in Singapore, the SMTP provider I was using has its servers located in the US, making the roundtrip connection take considerable time.
  3. No reusability of the SMTP connection
    When calling the function to send an email, the function sends the email immediately, creating a new SMTP connection on that moment (it doesn’t offer to collect all emails and send them all together at the end of the request, under a single SMTP connection).

Because of #1, the time the user must wait for the response is tied to the time it takes to send the emails. Because of #2, the time to send one email is relatively high. And because of #3, the time to send 20 emails is 20 times the time it takes to send one email. While sending only one email may not make the application terribly slower, sending 20 emails certainly does, affecting the user experience.

Let’s see how we can solve this issue.

Front-end is messy and complicated these days. That's why we publish articles, printed books and webinars with useful techniques to improve your work. Even better: Smashing Membership with a growing selection of front-end & UX goodies. So you get your work done, better and faster.

Explore Smashing Membership ↬ Paying Attention To The Nature Of Transactional Emails

Before anything, we must notice that not all emails are equal in importance. We can broadly categorize emails into two groups: priority and non-priority emails. For instance, if the user forgot the password to access the account, she will expect the email with the password reset link immediately on her inbox; that is a priority email. In contrast, sending an email notifying that somebody we follow has posted new content does not need to arrive on the user’s inbox immediately; that is a non-priority email.

The solution must optimize how these two categories of emails are sent. Assuming that there will only be a few (maybe 1 or 2) priority emails to be sent during the process, and the bulk of the emails will be non-priority ones, then we design the solution as follows:

  • Priority emails can simply avoid the high latency issue by using an SMTP provider located in the same region where the application is deployed. In addition to good research, this involves integrating our application with the provider’s API.
  • Non-priority emails can be sent asynchronously, and in batches where many emails are sent together. Implemented at the application level, it requires an appropriate technology stack.

Let’s define the technology stack to send emails asynchronously next.

Defining The Technology Stack

Note: I have decided to base my stack on AWS services because my website is already hosted on AWS EC2. Otherwise, I would have an overhead from moving data among several companies’ networks. However, we can implement our soluting using other cloud service providers too.

My first approach was to set-up a queue. Through a queue, I could have the application not send the emails anymore, but instead publish a message with the email content and metadata in a queue, and then have another process pick up the messages from the queue and send the emails.

However, when checking the queue service from AWS, called SQS, I decided that it was not an appropriate solution, because:

  • It is rather complex to set-up;
  • A standard queue message can store only up top 256 kb of information, which may not be enough if the email has attachments (an invoice for instance). And even though it is possible to split a large message into smaller messages, the complexity grows even more.

Then I realized that I could perfectly imitate the behavior of a queue through a combination of other AWS services, S3 and Lambda, which are much easier to set-up. S3, a cloud object storage solution to store and retrieve data, can act as the repository for uploading the messages, and Lambda, a computing service that runs code in response to events, can pick a message and execute an operation with it.

In other words, we can set-up our email sending process like this:

  1. The application uploads a file with the email content + metadata to an S3 bucket.
  2. Whenever a new file is uploaded into the S3 bucket, S3 triggers an event containing the path to the new file.
  3. A Lambda function picks the event, reads the file, and sends the email.

Finally, we have to decide how to send emails. We can either keep using the SMTP provider that we already have, having the Lambda function interact with their APIs, or use the AWS service for sending emails, called SES. Using SES has both benefits and drawbacks:

Benefits:
  • Very simple to use from within AWS Lambda (it just takes 2 lines of code).
  • It is cheaper: Lambda fees are computed based on the amount of time it takes to execute the function, so connecting to SES from within the AWS network will take a shorter time than connecting to an external server, making the function finish earlier and costing less. (Unless SES is not available in the same region where the application is hosted; in my case, because SES is not offered in the Asian Pacific (Singapore) region, where my EC2 server is located, then I might be better off connecting to some Asia-based external SMTP provider).
Drawbacks:
  • Not many stats for monitoring our sent emails are provided, and adding more powerful ones requires extra effort (eg: tracking what percentage of emails were opened, or what links were clicked, must be set-up through AWS CloudWatch).
  • If we keep using the SMTP provider for sending the priority emails, then we won’t have our stats all together in 1 place.

For simplicity, in the code below we will be using SES.

We have then defined the logic of the process and stack as follows: The application sends priority emails as usual, but for non-priority ones, it uploads a file with email content and metadata to S3; this file is asynchronously processed by a Lambda function, which connects to SES to send the email.

Let’s start implementing the solution.

Differentiating Between Priority And Non-Priority Emails

In short, this all depends on the application, so we need to decide on an email by email basis. I will describe a solution I implemented for WordPress, which requires some hacks around the constraints from function wp_mail. For other platforms, the strategy below will work too, but quite possibly there will be better strategies, which do not require hacks to work.

The way to send an email in WordPress is by calling function wp_mail, and we don’t want to change that (eg: by calling either function wp_mail_synchronous or wp_mail_asynchronous), so our implementation of wp_mail will need to handle both synchronous and asynchronous cases, and will need to know to which group the email belongs. Unluckily, wp_mail doesn’t offer any extra parameter from which we could asses this information, as it can be seen from its signature:

function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() )

Then, in order to find out the category of the email we add a hacky solution: by default, we make an email belong to the priority group, and if $to contains a particular email (eg: nonpriority@asynchronous.mail), or if $subject starts with a special string (eg: “[Non-priority!]“), then it belongs to the non-priority group (and we remove the corresponding email or string from the subject). wp_mail is a pluggable function, so we can override it simply by implementing a new function with the same signature on our functions.php file. Initially, it contains the same code of the original wp_mail function, located in file wp-includes/pluggable.php, to extract all parameters:

if ( !function_exists( 'wp_mail' ) ) : function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ) { $atts = apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments' ) ); if ( isset( $atts['to'] ) ) { $to = $atts['to']; } if ( !is_array( $to ) ) { $to = explode( ',', $to ); } if ( isset( $atts['subject'] ) ) { $subject = $atts['subject']; } if ( isset( $atts['message'] ) ) { $message = $atts['message']; } if ( isset( $atts['headers'] ) ) { $headers = $atts['headers']; } if ( isset( $atts['attachments'] ) ) { $attachments = $atts['attachments']; } if ( ! is_array( $attachments ) ) { $attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) ); } // Continue below... } endif;

And then we check if it is non-priority, in which case we then fork to a separate logic under function send_asynchronous_mail or, if it is not, we keep executing the same code as in the original wp_mail function:

function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ) { // Continued from above... $hacky_email = "nonpriority@asynchronous.mail"; if (in_array($hacky_email, $to)) { // Remove the hacky email from $to array_splice($to, array_search($hacky_email, $to), 1); // Fork to asynchronous logic return send_asynchronous_mail($to, $subject, $message, $headers, $attachments); } // Continue all code from original function in wp-includes/pluggable.php // ... }

In our function send_asynchronous_mail, instead of uploading the email straight to S3, we simply add the email to a global variable $emailqueue, from which we can upload all emails together to S3 in a single connection at the end of the request:

function send_asynchronous_mail($to, $subject, $message, $headers, $attachments) { global $emailqueue; if (!$emailqueue) { $emailqueue = array(); } // Add email to queue. Code continues below... }

We can upload one file per email, or we can bundle them so that in 1 file we contain many emails. Since $headers contains email meta (from, content-type and charset, CC, BCC, and reply-to fields), we can group emails together whenever they have the same $headers. This way, these emails can all be uploaded in the same file to S3, and the $headers meta information will be included only once in the file, instead of once per email:

function send_asynchronous_mail($to, $subject, $message, $headers, $attachments) { // Continued from above... // Add email to the queue $emailqueue[$headers] = $emailqueue[$headers] ?? array(); $emailqueue[$headers][] = array( 'to' => $to, 'subject' => $subject, 'message' => $message, 'attachments' => $attachments, ); // Code continues below }

Finally, function send_asynchronous_mail returns true. Please notice that this code is hacky: true would normally mean that the email was sent successfully, but in this case, it hasn’t even been sent yet, and it could perfectly fail. Because of this, the function calling wp_mail must not treat a true response as “the email was sent successfully,” but an acknowledgment that it has been enqueued. That’s why it is important to restrict this technique to non-priority emails so that if it fails, the process can keep retrying in the background, and the user will not expect the email to already be in her inbox:

function send_asynchronous_mail($to, $subject, $message, $headers, $attachments) { // Continued from above... // That's it! return true; } Uploading Emails To S3

In my previous article “Sharing Data Among Multiple Servers Through AWS S3”, I described how to create a bucket in S3, and how to upload files to the bucket through the SDK. All code below continues the implementation of a solution for WordPress, hence we connect to AWS using the SDK for PHP.

We can extend from the abstract class AWS_S3 (introduced in my previous article) to connect to S3 and upload the emails to a bucket “async-emails” at the end of the request (triggered through wp_footer hook). Please notice that we must keep the ACL as “private” since we don’t want the emails to be exposed to the internet:

class AsyncEmails_AWS_S3 extends AWS_S3 { function __construct() { // Send all emails at the end of the execution add_action("wp_footer", array($this, "upload_emails_to_s3"), PHP_INT_MAX); } protected function get_acl() { return "private"; } protected function get_bucket() { return "async-emails"; } function upload_emails_to_s3() { $s3Client = $this->get_s3_client(); // Code continued below... } } new AsyncEmails_AWS_S3();

We start iterating through the pairs of headers => emaildata saved in global variable $emailqueue, and get a default configuration from function get_default_email_meta for if the headers are empty. In the code below, I only retrieve the “from” field from the headers (the code to extract all headers can be copied from the original function wp_mail):

class AsyncEmails_AWS_S3 extends AWS_S3 { public function get_default_email_meta() { // Code continued from above... return array( 'from' => sprintf( '%s <%s>', get_bloginfo('name'), get_bloginfo('admin_email') ), 'contentType' => 'text/html', 'charset' => strtolower(get_option('blog_charset')) ); } public function upload_emails_to_s3() { // Code continued from above... global $emailqueue; foreach ($emailqueue as $headers => $emails) { $meta = $this->get_default_email_meta(); // Retrieve the "from" from the headers $regexp = '/From:\s*(([^\<]*?) <)?<?(.+?)>?\s*\n/i'; if(preg_match($regexp, $headers, $matches)) { $meta['from'] = sprintf( '%s <%s>', $matches[2], $matches[3] ); } // Code continued below... } } }

Finally, we upload the emails to S3. We decide how many emails to upload per file with the intention to save money. Lambda functions charge based on the amount of time they need to execute, calculated on spans of 100ms. The more time a function requires, the more expensive it becomes.

Sending all emails by uploading 1 file per email, then, is more expensive than uploading 1 file per many emails, since the overhead from executing the function is computed once per email, instead of only once for many emails, and also because sending many emails together fills the 100ms spans more thoroughly.

So we upload many emails per file. How many emails? Lambda functions have a maximum execution time (3 seconds by default), and if the operation fails, it will keep retrying from the beginning, not from where it failed. So, if the file contains 100 emails, and Lambda manages to send 50 emails before the max time is up, then it fails and it retries executing the operation again, sending the first 50 emails once again. To avoid this, we must choose a number of emails per file that we are confident is enough to process before the max time is up. In our situation, we could choose to send 25 emails per file. The number of emails depends on the application (bigger emails will take longer to be sent, and the time to send an email will depend on the infrastructure), so we should do some testing to come up with the right number.

The content of the file is simply a JSON object, containing the email meta under property “meta”, and the chunk of emails under property “emails”:

class AsyncEmails_AWS_S3 extends AWS_S3 { public function upload_emails_to_s3() { // Code continued from above... foreach ($emailqueue as $headers => $emails) { // Code continued from above... // Split the emails into chunks of no more than the value of constant EMAILS_PER_FILE: $chunks = array_chunk($emails, EMAILS_PER_FILE); $filename = time().rand(); for ($chunk_count = 0; $chunk_count < count($chunks); $chunk_count++) { $body = array( 'meta' => $meta, 'emails' => $chunks[$chunk_count], ); // Upload to S3 $s3Client->putObject([ 'ACL' => $this->get_acl(), 'Bucket' => $this->get_bucket(), 'Key' => $filename.$chunk_count.'.json', 'Body' => json_encode($body), ]); } } } }

For simplicity, in the code above, I am not uploading the attachments to S3. If our emails need to include attachments, then we must use SES function SendRawEmail instead of SendEmail (which is used in the Lambda script below).

Having added the logic to upload the files with emails to S3, we can move next to coding the Lambda function.

Coding The Lambda Script

Lambda functions are also called serverless functions, not because they do not run on a server, but because the developer does not need to worry about the server: the developer simply provides the script, and the cloud takes care of provisioning the server, deploying and running the script. Hence, as mentioned earlier, Lambda functions are charged based on function execution time.

The following Node.js script does the required job. Invoked by the S3 “Put” event, which indicates that a new object has been created on the bucket, the function:

  1. Obtains the new object’s path (under variable srcKey) and bucket (under variable srcBucket).
  2. Downloads the object, through s3.getObject.
  3. Parses the content of the object, through JSON.parse(response.Body.toString()), and extracts the emails and the email meta.
  4. Iterates through all the emails, and sends them through ses.sendEmail.
var async = require('async'); var aws = require('aws-sdk'); var s3 = new aws.S3(); exports.handler = function(event, context, callback) { var srcBucket = event.Records[0].s3.bucket.name; var srcKey = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " ")); // Download the file from S3, parse it, and send the emails async.waterfall([ function download(next) { // Download the file from S3 into a buffer. s3.getObject({ Bucket: srcBucket, Key: srcKey }, next); }, function process(response, next) { var file = JSON.parse(response.Body.toString()); var emails = file.emails; var emailsMeta = file.meta; // Check required parameters if (emails === null || emailsMeta === null) { callback('Bad Request: Missing required data: ' + response.Body.toString()); return; } if (emails.length === 0) { callback('Bad Request: No emails provided: ' + response.Body.toString()); return; } var totalEmails = emails.length; var sentEmails = 0; for (var i = 0; i < totalEmails; i++) { var email = emails[i]; var params = { Destination: { ToAddresses: email.to }, Message: { Subject: { Data: email.subject, Charset: emailsMeta.charset } }, Source: emailsMeta.from }; if (emailsMeta.contentType == 'text/html') { params.Message.Body = { Html: { Data: email.message, Charset: emailsMeta.charset } }; } else { params.Message.Body = { Text: { Data: email.message, Charset: emailsMeta.charset } }; } // Send the email var ses = new aws.SES({ "region": "us-east-1" }); ses.sendEmail(params, function(err, data) { if (err) { console.error('Unable to send email due to an error: ' + err); callback(err); } sentEmails++; if (sentEmails == totalEmails) { next(); } }); } } ], function (err) { if (err) { console.error('Unable to send emails due to an error: ' + err); callback(err); } // Success callback(null); }); };

Next, we must upload and configure the Lambda function to AWS, which involves:

  1. Creating an execution role granting Lambda permissions to access S3.
  2. Creating a .zip package containing all the code, i.e. the Lambda function we are creating + all the required Node.js modules.
  3. Uploading this package to AWS using a CLI tool.

How to do these things is properly explained on the AWS site, on the Tutorial on Using AWS Lambda with Amazon S3.

Hooking Up S3 With The Lambda Function

Finally, having the bucket and the Lambda function created, we need to hook both of them together, so that whenever there is a new object created on the bucket, it will trigger an event to execute the Lambda function. To do this, we go to the S3 dashboard and click on the bucket row, which will show its properties:

Clicking on the bucket's row displays the bucket's properties. (Large preview)

Then clicking on Properties, we scroll down to the item “Events”, and there we click on Add a notification, and input the following fields:

  • Name: name of the notification, eg: “EmailSender”;
  • Events: “Put”, which is the event triggered when a new object is created on the bucket;
  • Send to: “Lambda Function”;
  • Lambda: name of our newly created Lambda, eg: “LambdaEmailSender”.
Adding a notification in S3 to trigger an event for Lambda. (Large preview)

Finally, we can also set the S3 bucket to automatically delete the files containing the email data after some time. For this, we go to the Management tab of the bucket, and create a new Lifecycle rule, defining after how many days the emails must expire:

Setting up a Lifecycle rule to automatically delete files from the bucket. (Large preview)

That’s it. From this moment, when adding a new object on the S3 bucket with the content and meta for the emails, it will trigger the Lambda function, which will read the file and connect to SES to send the emails.

I implemented this solution on my site, and it became fast once again: by offloading sending emails to an external process, whether the applications send 20 or 5000 emails doesn’t make a difference, the response to the user who triggered the action will be immediate.

Conclusion

In this article we have analyzed why sending many transactional emails in a single request may become a bottleneck in the application, and created a solution to deal with the issue: instead of connecting to the SMTP server from within the application (synchronously), we can send the emails from an external function, asynchronously, based on a stack of AWS S3 + Lambda + SES.

By sending emails asynchronously, the application can manage to send thousands of emails, yet the response to the user who triggered the action will not be affected. However, to ensure that the user is not waiting for the email to arrive in the inbox, we also decided to split emails into two groups, priority and non-priority, and send only the non-priority emails asynchronously. We provided an implementation for WordPress, which is rather hacky due to the limitations of function wp_mail for sending emails.

A lesson from this article is that serverless functionalities on a server-based application work pretty well: sites running on a CMS like WordPress can improve their performance by implementing only specific features on the cloud, and avoid a great deal of complexity that comes from migrating highly dynamic sites to a fully serverless architecture.

(rb, ra, yk, il)
Categories: Around The Web

Using Visual Composer Website Builder To Create WordPress Websites

Smashing Magazine - Mon, 11/12/2018 - 6:55am
Using Visual Composer Website Builder To Create WordPress Websites Using Visual Composer Website Builder To Create WordPress Websites Nick Babich 2018-11-12T12:55:03+01:00 2018-11-14T13:45:20+00:00

(This is a sponsored article.) WordPress has changed the way we make websites and millions of people use it to create websites today. But this tool has a few significant limitations — it requires time and coding skills to create a website.

Even when you have aquired coding skills, jumping into code each time when you need to solve a problem (add a new UI element or change styling options for existing one) can be tedious. All too often we hear: “We need to work harder to achieve our goals.” While working hard is definitely important we also need to work smarter.

Today, I’d like to review a tool that will allow us to work smarter. Imagine WordPress without design and technical limits; the tool that reduces the need to hand-code the parts of your website and frees you up to work on more interesting and valuable parts of the design.

In this article, I’ll review the Visual Composer Website Builder and create a real-world example — a landing page for a digital product — just by using this tool.

What Is Visual Composer Website Builder?

Visual Composer Website Builder is a simple and powerful drag-and-drop website builder that promises to change the way we work with WordPress. It introduced a more intuitive way of building a page — all actions involving changing visual hierarchy and content management are done visually. The tool reduces the need to hand-code the theme parts of a website and frees you up to work on valuable parts of the design such as content.

(Large preview)

Content is the most important property of your website. It’s the primary reason why people visit your site — for content. It’s worth putting a lot of efforts in creating good content and use tools that help you to deliver the content in the best way to visitors with the least amount of effort.

Visual Composer And WPBakery

Visual Composer Website Builder is a builder from the creators of WPBakery Page Builder. If you had a chance to use the WPBakery Page builder before you might wonder what the difference between the two plugins is. Let’s be clear about these two products:

There are a few significant differences between the two:.

  • The key difference between WPBakery Page builder and Visual Composer is that WPBakery is only for the content part, while with Visual Composer Website Builder you can create a complete website (including Headers and Footers).
  • Visual Composer is not shortcode based, which helps generate clean code. Also, disabling the plugin won’t leave you with “shortcode hell” (a situation when shortcodes can’t be rendered without an activated plugin).

You can check the full list of differences between two plugins here.

Now, Visual Composer Website Builder is not an ‘advanced’ version of WPBakery. It is an entirely new product that was created to satisfy the growing needs of web professionals. Visual Composer is not just a plugin; it’s a powerful platform that can be extended as user needs continue evolving.

A Quick List Of Visual Composer’s Features

While I’ll show you how Visual Composer works in action below, it’s worth to point out a few key benefits of this tool:

  • It’s a live-preview editor with drag-and-drop features, and hundreds of ready-to-use content elements that bring a lot of design freedom. You can make changes instantly and see end-results before publishing.
  • Two ways of page editing — using frontend editor and tree view. Tree view allows navigating through the elements available on a page and makes a design process much easier.
  • Ready-to-use WordPress templates for all types of pages — from landing pages and portfolios to business websites with dedicated product pages, because editing an existing template is a lot easier than starting from scratch with a blank page.
  • Visual Composer works with any theme (i.e. it’s possible to integrate Visual Composer Website builder into your existing themes)
  • Responsive design out-of-the-box. All the elements and templates are responsive and mobile-ready. You can adjust responsiveness for each independent column.
  • Header, footer, and sidebar editor. Usually the header, footer and sidebar are defined by the theme you’re using. When web professionals need to change them, they usually move to code. But with Visual Composer, you can change the layout quickly and easily using only the visual editor. This feature is available in the Premium version of the product.
  • An impressive collection of add-ons (it’s possible to get add-ons from the Hub or get them from third-party developers)

There are also three features that make Visual Composer stand out from the crowd. Here they are:

1. Visual Composer Hub

Visual Composer Hub is a cloud which stores all the elements available to the users. It’s basically like a design system that keeps itself updated and where you can get new elements, templates, elements, blocks (soon).

(Large preview)

The great thing about Visual Composer Hub is that you don’t need to update the plugin to get new elements — you can download the elements whenever you need them. As a result, your WP setup isn’t bloated with a myriad of unused elements.

2. New Technical Stack

Visual Composer Website builder is built on a new technology stack — it’s powered by ReactJS and doesn’t use any of the WordPress shortcodes. This helps to achieve better performance — the team behind Visual Composer conducted a series of internal tests and showed that pages created with Visual Composer load 1-1.5s faster than the same layouts re-created with WPBakery.

3. API

Visual Composer Website builder has a well-documented open API. If you have coding skills, you can extend Visual Composer with your own custom elements which may be helpful for some custom projects.

How To Create A Landing Page With Visual Composer

In this section, I’ll show how to create a landing page for a digital product called CalmPod (a fictional home speaker device) with the new Visual Composer Website Builder.

Our journey starts in a WP interface where we need to create a new page — give it a title and click the ‘Edit with Visual Composer button.’

(Large preview) Creating A Layout For A Landing Page

The process of creating the page starts with building an appropriate layout. Usually building a layout for a landing page takes a lot of time and effort. Designers have to try a lot of different approaches before finding the one that works the best for the content. But Visual Composer simplifies the task for designers — it provides a list of ready-to-use layouts (available under the Add Template option). So, all you need to do to create a new page is to find the appropriate layout from the list of available options and see how it works for your content.

You can start with a blank page or select a ready-to-use template. (Large preview)

But for our example, we’ll select the Startup Page template. This template applies automatically as soon as we click the + symbol, so all we need to do is to modify it according to our needs.

(Large preview)

Each layout in Visual Composer consists of rows and columns. The row is a base that defines the logical structure of the page. Each row consists of columns. Visual Composer gives you the ability to control the number of columns in a row.

(Large preview)

Tip: Notice that Visual Composer uses different colored borders for UI units. When we select a row, we see a blue-colored border, when we select a column, we see an orange-colored border. This feature can be extremely valuable when you work on creating complex layouts.

(Large preview) (Large preview)

The great thing about Visual Composer is that we can customize all properties of the layout — add/remove elements or change their styling options (such as margins, padding between elements). For example, we don’t need to dive into the code to alter the sizes of columns; we can simply drag and drop the borders of individual elements.

(Large preview)

It’s important to mention that we can use either the visual editor or the tree view of elements to modify individual properties of UI elements.

(Large preview)

By clicking on the ‘Pen’ icon, we activate a screen with individual styling properties for the element.

(Large preview) Stretch Content

Visual Composer allows making the layout either boxed or stretched. If you switch the ‘Stretch content’ toggle to ‘On’, your layout will be in full width.

(Large preview) Changing The Page Title

Visual Composer allows users to change the page title. You can do it in the Layout settings. Let’s give our page the following title: ‘CalmTech: the best digital assistant.’

(Large preview) Adding The Top Menu

Now it’s time to add a top menu to our landing page. Suppose we have the following menu in WP:

(Large preview)

And we want to place it at the top of our newly created landing page. To do that, we need to go to Visual Composer -> Headers (because the top of the page is a default place for navigation) and create a new header.

As soon as we click on the ‘Add Header’ button, we’ll see a screen that asks us to provide a title for the page — let’s give it a name “Top header.” It’s a technical name that will help us identify this object.

(Large preview)

Next, Visual Composer will direct us to the Hub where we can add all required UI elements to our header. Since we want to have a menu, we type ‘menu’ in the search box. The Hub provides us with two options: Basic menu and Sandwich menu. For our case, we’ll use the* Basic Menu* because we have a limited number of top level navigation options and want all of them to be visible all the time (hidden navigation such as Sandwich Menu can be bad for discoverability) .

(Large preview)

Finally, we need to choose the menu source (in our case it’ll be Main menu, the one that we have in WP) and change the appearance of the navigation options.

(Large preview)

Let’s change the alignment of the menu (we will move it to the right).

(Large preview)

And that’s all. Now we can use our header page settings. Let’s modify our home page to include a Header. Hover over the *Please select Your Header*element, and you’ll see a button Add Header.

(Large preview)

When you click on the button, you’ll see a dialog at the left part of the screen that invites you to select a header. Let’s choose the Top Header option from the list.

(Large preview)

After we select a header, you’ll see a menu at the top of the page.

(Large preview) Making The Top Menu Sticky

The foundational principle of good navigation says that a navigation menu should be available for the users all of the time. But unfortunately, on many websites, the top navigation menu hides when you scroll. Such behavior forces users to scroll way back to the top in order to navigate to another page. It introduces unnecessary interaction costs. Fortunately, there’s a simple solution for this problem — we can make the top menu sticky. A sticky menu stays visible all the time no matter where the user is on a page.

To enable stickiness, we need to turn the Sticky toggle for our header On (for the whole Menu container) and add a margin 50-pixels margin to the Margin top.

(Large preview)

When you scroll the landing page, you’ll notice that the header stays visible all the time.

Pairing Image With Text

Next comes a really exciting part — we need to describe our product to our visitors. To create a great first-time impression, we need to provide excellent images paired with a clear description. Text description and product picture (or pictures) should work together and engage visitors in learning more about a product.

We need to replace a default image with our image. Click on the image and upload a new one. We’ll use an image with a dart background, so we also need to change the background for the container. We need to select the row and modify the background color option.

(Large preview)

Next, we need to add a text section to the left of the image. In the western world, users scan the page from left to right, so visitors will read text description and match it with the image. Visual Composer uses Text Block object to store the text information. Let’s replace a text that came with theme with our custom text “CalmTech A breakthrough speaker that adapts to its location.” Let’s also modify the text color to make the text more relevant to the theme (white for the title and a shade of gray for the subtitle).

(Large preview) Creating A Group Of Elements

We have a picture of a product and a text description, but still, one element is missing. As you probably guessed, it’s a call to action (CTA). Good designers don’t just create individual pages but a holistic user journey. Thus, to create an enjoyable user journey, it’s important to guide users along the way. At the time when visitors read the necessary information, it’s vital to provide the next logical step for them, and a CTA is a precisely right element for this role.

In our case, we’ll need two CTAs — ‘Buy now’ and ‘Learn More.’ The primary call to action button “Buy now” should come first and it should be more eye-catching (we expect that users will click on it). Thus, we need to make it more contrasting while the “Learn more” button should be a hollow button.

Visual Composer makes it easier to customize the general parameters for the UI element (such as a gap) as well as individual styling options. Since we’re interested in changing individual properties, we need to click on ‘Edit’ for a particular button.

(Large preview) Playing With Animation To Convey Dynamics And Telling Stories

People visit dozens of different websites on a daily basis. In such a highly competitive market web professionals need to create genuinely memorable products. One way to achieve this goal is to focus on building better user engagement.

It’s possible to engage visitors to interact with a product by conveying some dynamics. If you make a site less static, there’s a better chance that visitors remember it.

Visual Composer allows you to choose from a few predefined CSS animations of a particular element. When we open design options for any UI element we can find the option Animate. When we choose the animated option, it’ll be triggered once the element will be visible in the browser window.

(Large preview) Final Polishing

Let’s see how our page looks like for our site’s visitors. It’s obvious that it has two problems:

  • It looks a bit unfinished (we don’t have a logo of a website),
  • The elements have the wrong proportions (the text overpowers the image so the layout looks unbalanced).
(Large preview)

Let’s solve the first problem. Go to the Headers section and select our Top Header. Click on ‘+’ element and select an object Single Image. Upload new image (the icon). Notice that we can modify the size of the image right in the Visual Composer. Let’s make the size of our icon 50px x 50px (in the Size section).

(Large preview)

Now it’s time to solve the second problem. Select the first column and adjust the size of an text (set the size to 40 for the subheader). Here is how our page will look like after the changes.

(Large preview) Conclusion

Visual Composer Website Builder simplifies the process of page building in WordPress. The process of web design becomes not only fast and easy, but it also becomes more fun because designers have a lot more creative freedom to express their ideas. And when web professionals have more creative freedom, they can come up with better design solutions.

(ms, ra, il)
Categories: Around The Web

CSS Frameworks Or CSS Grid: What Should I Use For My Project?

Smashing Magazine - Fri, 11/09/2018 - 5:30am
CSS Frameworks Or CSS Grid: What Should I Use For My Project? CSS Frameworks Or CSS Grid: What Should I Use For My Project? Rachel Andrew 2018-11-09T12:30:30+02:00 2018-11-14T13:45:20+00:00

Among the questions I am most frequently asked is some variety of the question, “Should I use CSS Grid or Bootstrap?” In this article, I will take a look at that question. You will discover that the reasons for using frameworks are varied, and not simply centered around use of the grid system contained in that framework. I hope that by unpacking these reasons, I can help you to make your own decision, in terms of what is best for the sites and applications that you are working on, and also for the team you work with.

In this article when I talk about a framework, I’m describing a third party CSS framework such as Bootstrap or Foundation. You might argue these are really component libraries, but many people (including their own docs) would describe them as a framework so that is what we will use here. The important factor is that these are something developed externally to you, without reference to your specific issues. The alternative to using a third party framework is to write your own CSS — that might involve developing your own internal framework, using a bunch of common files as a starting point, or creating every project as a new thing. All these things are done in reference to your own specific needs rather than very generic ones.

Why Choose A CSS Framework?

The question of whether to use Grid or a framework is flawed, as CSS Grid is not a drop-in replacement for the things that a CSS framework does. Any exploration of the subject needs to consider what of our framework CSS Grid is going to replace. I wanted to start by finding out why people had chosen to use a CSS framework at all. So I turned to Twitter and posted this tweet.

A question: if you have chosen to use a CSS framework (Bootstrap, Foundation etc.) for your project, what were the main reasons for doing so?

— Rachel Andrew (@rachelandrew) October 16, 2018

There were a lot of responses. As I expected, there are far more reasons to use a framework than simply the grid system that it contains.

Getting workflow just right ain’t an easy task. So are proper estimates. Or alignment among different departments. That’s why we’ve set up “this-is-how-I-work”-sessions — with smart cookies sharing what works well for them. A part of the Smashing Membership, of course.

Explore Smashing Membership ↬ A Framework Gives Your Team Ready Made Documentation

If you are working on a project with a number of other developers then any internal system you create will need to also include documentation to help your team members use it effectively. Creating useful documentation is time-consuming, skilled work in itself, and something that the big frameworks do very well.

The Bootstrap Documentation. (Large preview)

Framework documentation came up again and again, with many experienced front-end developers chipping in and explaining this is why they would recommend and use a CSS framework. I sometimes hear the opinion that people are using frameworks because they don’t really know CSS, many of the people replying, however, are well known to me as expert CSS developers. I’m sure that they are sometimes frustrated by the choices made by the framework, however, the positive aspects of that choice outweigh that.

Online Communities: Easy Access To Help

When you decide to use a particular tool, you also gain a community of users to ask for help. Unless you have a very clear CSS issue, and can produce a reduced use case to demonstrate it, asking for help with CSS can be difficult. It is especially so if you want to ask how to approach building a certain component. Using a framework can give you a starting point for your question; in general, you will be asking how to modify or style a particular component rather than starting from scratch. This is an easier thing to ask, as well as an easier thing to answer.

The Grid System

Despite the fact that we have CSS Grid, many people replied that the main reason they decided to use a framework was for the grid system. Of course, many of these projects may have been started a long time before CSS Grid was available. Even today, however, concerns about backwards compatibility or team understanding of newer layout methods might cause people to decide to use a framework rather than adopting native CSS.

Speed Of Project Delivery

when having a unique design and efficient css were a much lower priority than the due date

— CodingBlocks.net (@CodingBlocks) October 16, 2018

Opting for a framework will, in general, make it far quicker to deliver your project, in particular if that project fits very well with the way the framework does things and doesn’t need a lot of customization.

In the case of developing an MVP for a new idea, a framework may well be an excellent choice. You will have many things to spend time on, and be still testing assumptions in terms of what the project needs. Being able to develop that first version using a framework can help you get the product in front of users more quickly, and save burning up a lot of time developing things you then decide not to use.

I go with a framework on anything that’s an MVP — minimal dev effort on tooling and frameworks to optimise for time working on the actual concept.

I make my own CSS “framework” on anything that’s a redo of an existing large-scale site/app.

— Ben Bodien (@bbodien) October 16, 2018

Another place where speed and a bunch of ready built components can be very useful is when developing the backend admin system for a site or application. In the case where you simply need to create a few admin screens, a framework can save a lot of time styling form fields and other components! There are even dashboard themes for Bootstrap and Foundation that can give a helpful starting point.

Collections of dasboard components make it quicker to build out the admin for an app. (Large preview) I’m Not A Designer!

This point is the reason I’ve opted for a CSS framework in the past. I’m not a designer, and if I have to both design and build something, I’ll spend a long time trying to make design decisions I am entirely unqualified to make. It would be lovely to have the funds to hire a designer for every side project, however, I don’t, and so a framework might mean the difference between shipping the thing and not.

Dealing With CSS Bugs And Browser Compatibility Issues

Mentioned less than I thought it might be was the fact that the framework authors would already have dealt with browser issues, be that due to actual bugs or lack of support for certain features. However, this was still a factor in the decision-making for many people.

To Help With Responsive Design

Responsiveness of web pages . I found it difficult for me to decide breakpoints for web pages.

— Faisal Ali (@f_a_akhtar) October 18, 2018

This came up a few times; people were opting for a framework specifically due to the fact it was responsive, or that I made decisions about breakpoints for them. I thought it interesting that this specifically was something called out as a factor in choosing to use a framework.

Why Not Use A Framework?

Among positive reasons why frameworks had been selected were some of the issues that people have had with that choice.

Difficulty Of Overriding Framework Code

Many people commented on the fact that it could become difficult to override the code used in the framework, and that frameworks were a good choice if they didn’t need a lot of overriding. The benefits of ease of use, and everyone on the team understanding how to use the framework can be lost if there are then a huge number of customizations in place.

All Websites End Up Looking The Same

The blame for all websites starting to look the same has been placed squarely at the door of the well known CSS frameworks. I have seen sites where I am sure a certain framework has been used, then discover they are custom CSS, so prevalent are the design choices made in these frameworks.

The difficulty in overriding framework styles already mentioned is a large part of why sites developed using a particular framework will tend to look similar. This isn’t just a creative issue, it can be very odd as a user of a few websites which have all opted for the same framework to feel that they are all the same thing. In terms of conveying your brand, and making good user experience part of that, perhaps you lose something when opting for the generic choices of a framework.

Inheriting The CSS Problems Of The Entire World

Whether front or back-end, any tool or framework that seeks to hit the mainstream has to solve as many problems as possible. Unless the tool is tightly coupled to solving one particular use-case it is going to contain a lot of very generic code, and code which solves problems that you do not have, and will never have.

You may be in the fortunate position of only needing your full experience to be viewed in the most current browsers, allowing for a more limited experience in Internet Explorer, or older versions of Chrome. Using a framework with lots of built-in support going back to IE9 would result in lots of additional code — especially given the improvements in CSS layout recently. It might also prevent you from being creative, as everything in the framework is assuming this requirement for support. Things which are possible using CSS may well be limited by the framework.

As an example, the grid systems in popular frameworks do not have an ability to span rows, as there isn’t any concept or rows in layout systems prior to Grid Layout. CSS Grid Layout easily allows for this. If you are tied to the Bootstrap Grid and your designer comes up with a design that includes elements which span rows, you are left unable to implement it — despite the fact that Grid might be supported by your target browsers.

Performance Issues

Related to the above are performance issues inherent in using fairly generic code, rather than something optimized for the exact use cases that you have. When trying to improve performance you will find yourself hitting up against the decisions of the framework.

Increased Technical Debt

Speed, mainly, though we quickly found out it was a bit of a false economy because it created substantial technical debt in the long run.

— Tim Cthulhuegdon (@nefarioustim) October 16, 2018

While a framework might be a great way to get your startup quickly off the ground, and at the time of making that decision you are sure that you will replace it, is there a plan to make that happen?

Learning A Framework Rather Than Learning CSS

When talking to conference and workshop attendees, I have discovered that many people have only ever used a framework to write CSS. There is nothing wrong with coming into web development via one of these tools, given the complexity of the web platform today I imagine that will be the route in for many people. However, it can become a career-limiting choice, especially if the framework you based your skills around falls out of favor.

Having front-end developers without CSS knowledge should worry a company. It makes it incredibly hard to move away from that framework if your team doesn’t actually understand how to do CSS without it. While this isn’t really a reason not to use a framework, it is something to bear in mind when using one. When the day comes to move away you would hope that the team will be ready to take on something new, not needing to remember (or learn for the first time) how to write CSS!

The Choice Doesn’t Consider End Users

Nicole Sullivan asked pretty much the same question a few days prior to my question as I was thinking about writing this article, although she was considering front-end frameworks as a whole rather than just CSS frameworks. Jeremy Keith noted that precisely zero of the answers concerned end users. This was also the case with the responses to my question.

In our race to get our site built quickly, our desire to make things as good as possible for ourselves as the designers and developers of the site, do we forget who we are doing this for? Do the decisions made by the framework developer match up with the needs of the users of the site you are building?

Can We Replace Frameworks With “Vanilla” CSS?

If you are considering replacing your framework or starting a new project without one, what are some of the things that you could consider in order to make that process easier?

Understand Which Parts Of The Framework You Need

If you are replacing the use of a framework with your own CSS, a good place to start would be to audit your use of the current framework. Work out what you are using and why. Consider how you will replace those things in the new design.

You could follow a similar process when thinking about whether to select a framework or write your own. What parts of this could you reasonably expect to need? How well does it fit with your requirements? Will there be a lot of code that you import, potentially ask visitors to download, but never make use of?

Create A Documented Pattern Library Or Style Guide

I am a huge fan of working with pattern libraries and you can read my post here on Smashing Magazine about our use of Fractal. A pattern library or a style guide enables the creation of documentation along with all of your components. I start all of my projects by working on the CSS in the pattern library.

You are still going to need to write the documentation, as someone who writes documentation, however, I know that often the hardest thing is knowing where to start and how to structure the docs. A pattern library helps with this by keeping the docs along with the CSS for the component itself. This approach can also help prevent the docs becoming out of date as they are tightly linked to the component they refer to.

Develop Your Own CSS Code Guidelines

Consistency across the team is incredibly useful, and without a framework, there may be nothing dictating that. With newer layout methods, in particular, there are often several ways in which a pattern could be built, if everyone picks a different one then inconsistencies are likely to creep in.

Better Places To Ask For Help

Other than sending people in the direction of Stack Overflow, it seems that there are very few places to ask for help on CSS. In particular there seem to be few places which are approachable for beginners. If we are to encourage people away from third-party tools then we need to fill that need for friendly, helpful support which comes from the communities around those tools.

Within a company, it is possible that more experienced developers can become the CSS support for newer team members. If moving away from a framework to your own solution, it would be wise to consider what training might be needed to help bridge the gap, especially if people are used to using the help provided around the third party tool when they needed help in the past.

Style Guides Or Starting Points For Non-Designers

I tie myself in knots with questions such as, “Which fonts should I use?”, “How big should the headings be in relationship to the body text?”, “Is it OK to use a drop shadow?” I can easily write the CSS — if I know what I’m trying to do! What I really need are some rules for making a not-terrible design, some kind of typography starting point, or a set of basic guidelines would replace being able to use the defaults of a framework in a lot of cases.

Educating People About The State Of Modern Browser Interoperability

I have discovered that people who have been immersed in framework-based development for a number of years, often have a view of browser interoperability which is several years out of date. We have never been in a better situation in terms of CSS working cross-browser. It may be that some browsers don’t support one new shiny bit of CSS, but in general, CSS (when supported) won’t be full of strange bugs. For example, in almost all cases if you use CSS Grid in one browser your CSS will work in exactly the same way in another.

If trying to make a case for not using a framework to team members who believe that the framework saves them from browser bugs, this point may be a useful one to raise. Are the browser compatibility problems real, or based on the concerns of the past?

Will We See A New Breed Of Frameworks?

Something that interests me is whether our new layout methods will help usher in a new breed of tools and frameworks. Will we see tools which take advantage of new layout methods, allow for more creativity but still give teams and individuals some of the undeniable advantages that came out of the responses to my tweet.

Perhaps by relying on new layout methods, rather than an inbuilt grid system, a new-style framework could be much lighter, becoming a collection of useful components. It might be able to then get away from some of the performance issues inherent in very generic code.

An area a framework could help with would be in helping to create solid fallbacks for browsers which don’t support newer layout methods, or by having really solid accessibility baked into the components. This could help provide guidance into a way of working that considers interoperability and accessibility, even for those people who don’t have these things in the forefront of their minds.

I don’t think that simply switching the Bootstrap Grid for CSS Grid Layout will achieve this. Instead, authors coming up with new frameworks, should perhaps look at some of the reasons outlined here, and try to solve them in new ways, using the new functionality we have in CSS to do that.

Should You Use A Framework?

You and your team will need to answer that question yourself. And, despite what people might try to have you believe, there is no universal right or wrong answer. There is only what is right or wrong for your project. I hope that this article and the many responses to my original question might give you some things to discuss as you ponder that question.

Remember that the answer will change over time. It might be a useful thought experiment to not only consider what you need right now, in terms of initially doing the development for the site, but consider the lifespan of the site. Do you expect this still to be around in five years? Will your choice be a positive or negative one then?

Document your decisions, don’t be afraid to revisit them, and do ensure that you and your team maintain your skills outside of any framework that you decide to use. That way, you will be in a good place to move on in future and to make the best decisions for the next phases of your project.

I’d love the conversation started in that tweet to continue. Let us know your stories in the comments — they may well help other folks trying to work out what is best for their projects right now.

(il)
Categories: Around The Web

Sharing Data Among Multiple Servers Through AWS S3

Smashing Magazine - Thu, 11/08/2018 - 6:30am
Sharing Data Among Multiple Servers Through AWS S3 Sharing Data Among Multiple Servers Through AWS S3 Leonardo Losoviz 2018-11-08T12:30:30+01:00 2018-11-14T13:45:20+00:00

When providing some functionality for processing a file uploaded by the user, the file must be available to the process throughout the execution. A simple upload and save operation presents no issues. However, if in addition the file must be manipulated before being saved, and the application is running on several servers behind a load balancer, then we need to make sure that the file is available to whichever server is running the process at each time.

For instance, a multi-step “Upload your user avatar” functionality may require the user to upload an avatar on step 1, crop it on step 2, and finally save it on step 3. After the file is uploaded to a server on step 1, the file must be available to whichever server handles the request for steps 2 and 3, which may or may not be the same one for step 1.

A naive approach would be to copy the uploaded file on step 1 to all other servers, so the file would be available on all of them. However, this approach is not just extremely complex but also unfeasible: for instance, if the site runs on hundreds of servers, from several regions, then it cannot be accomplished.

A possible solution is to enable “sticky sessions” on the load balancer, which will always assign the same server for a given session. Then, steps 1, 2 and 3 will be handled by the same server, and the file uploaded to this server on step 1 will still be there for steps 2 and 3. However, sticky sessions are not fully reliable: If in between steps 1 and 2 that server crashed, then the load balancer will have to assign a different server, disrupting the functionality and the user experience. Likewise, always assigning the same server for a session may, under special circumstances, lead to slower response times from an overburdened server.

A more proper solution is to keep a copy of the file on a repository accessible to all servers. Then, after the file is uploaded to the server on step 1, this server will upload it to the repository (or, alternatively, the file could be uploaded to the repository directly from the client, bypassing the server); the server handling step 2 will download the file from the repository, manipulate it, and upload it there again; and finally the server handling step 3 will download it from the repository and save it.

Getting workflow just right ain’t an easy task. So are proper estimates. Or alignment among different departments. That’s why we’ve set up “this-is-how-I-work”-sessions — with smart cookies sharing what works well for them. A part of the Smashing Membership, of course.

Explore Smashing Membership ↬

In this article, I will describe this latter solution, based on a WordPress application storing files on Amazon Web Services (AWS) Simple Storage Service (S3) (a cloud object storage solution to store and retrieve data), operating through the AWS SDK.

Note 1: For a simple functionality such as cropping avatars, another solution would be to completely bypass the server, and implement it directly in the cloud through Lambda functions. But since this article is about connecting an application running on the server with AWS S3, we don’t consider this solution.

Note 2: In order to use AWS S3 (or any other of the AWS services) we will need to have a user account. Amazon offers a free tier here for 1 year, which is good enough for experimenting with their services.

Note 3: There are 3rd party plugins for uploading files from WordPress to S3. One such plugin is WP Media Offload (the lite version is available here), which provides a great feature: it seamlessly transfers files uploaded to the Media Library to an S3 bucket, which allows to decouple the contents of the site (such as everything under /wp-content/uploads) from the application code. By decoupling contents and code, we are able to deploy our WordPress application using Git (otherwise we cannot since user-uploaded content is not hosted on the Git repository), and host the application on multiple servers (otherwise, each server would need to keep a copy of all user-uploaded content.)

Creating The Bucket

When creating the bucket, we need to pay consideration to the bucket name: Each bucket name must be globally unique on the AWS network, so even though we would like to call our bucket something simple like “avatars”, that name may already be taken, then we may choose something more distinctive like “avatars-name-of-my-company”.

We will also need to select the region where the bucket is based (the region is the physical location where the data center is located, with locations all over the world.)

The region must be the same one as where our application is deployed, so that accessing S3 during the process execution is fast. Otherwise, the user may have to wait extra seconds from uploading/downloading an image to/from a distant location.

Note: It makes sense to use S3 as the cloud object storage solution only if we also use Amazon’s service for virtual servers on the cloud, EC2, for running the application. If instead, we rely on some other company for hosting the application, such as Microsoft Azure or DigitalOcean, then we should also use their cloud object storage services. Otherwise, our site will suffer an overhead from data traveling among different companies’ networks.

In the screenshots below we will see how to create the bucket where to upload the user avatars for cropping. We first head to the S3 dashboard and click on “Create bucket”:

S3 dashboard, showing all our existing buckets. (Large preview)

Then we type in the bucket name (in this case, “avatars-smashing”) and choose the region (“EU (Frankfurt)”):

Creating a bucket through in S3. (Large preview)

Only the bucket name and region are mandatory. For the following steps we can keep the default options, so we click on “Next” until finally clicking on “Create bucket”, and with that, we will have the bucket created.

Setting Up The User Permissions

When connecting to AWS through the SDK, we will be required to enter our user credentials (a pair of access key ID and secret access key), to validate that we have access to the requested services and objects. User permissions can be very general (an “admin” role can do everything) or very granular, just granting permission to the specific operations needed and nothing else.

As a general rule, the more specific our granted permissions, the better, as to avoid security issues. When creating the new user, we will need to create a policy, which is a simple JSON document listing the permissions to be granted to the user. In our case, our user permissions will grant access to S3, for bucket “avatars-smashing”, for the operations of “Put” (for uploading an object), “Get” (for downloading an object), and “List” (for listing all the objects in the bucket), resulting in the following policy:

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:Put*", "s3:Get*", "s3:List*" ], "Resource": [ "arn:aws:s3:::avatars-smashing", "arn:aws:s3:::avatars-smashing/*" ] } ] }

In the screenshots below, we can see how to add user permissions. We must go to the Identity and Access Management (IAM) dashboard:

IAM dashboard, listing all the users we have created. (Large preview)

In the dashboard, we click on “Users” and immediately after on “Add User”. In the Add User page, we choose a user name (“crop-avatars”), and tick on “Programmatic access” as the Access type, which will provide the access key ID and secret access key for connecting through the SDK:

Adding a new user. (Large preview)

We then click on button “Next: Permissions”, click on “Attach existing policies directly”, and click on “Create policy”. This will open a new tab in the browser, with the Create policy page. We click on the JSON tab, and enter the JSON code for the policy defined above:

Creating a policy granting 'Get', 'Post' and 'List' operations on the 'avatars-smashing' bucket. (Large preview)

We then click on Review policy, give it a name (“CropAvatars”), and finally click on Create policy. Having the policy created, we switch back to the previous tab, select the CropAvatars policy (we may need to refresh the list of policies to see it), click on Next: Review, and finally on Create user. After this is done, we can finally download the access key ID and secret access key (please notice that these credentials are available for this unique moment; if we don’t copy or download them now, we’ll have to create a new pair):

After the user is created, we are offered a unique time to download the credentials. (Large preview) Connecting To AWS Through The SDK

The SDK is available through a myriad of languages. For a WordPress application, we require the SDK for PHP which can be downloaded from here, and instructions on how to install it are here.

Once we have the bucket created, the user credentials ready, and the SDK installed, we can start uploading files to S3.

Uploading And Downloading Files

For convenience, we define the user credentials and the region as constants in the wp-config.php file:

define ('AWS_ACCESS_KEY_ID', '...'); // Your access key id define ('AWS_SECRET_ACCESS_KEY', '...'); // Your secret access key define ('AWS_REGION', 'eu-central-1'); // Region where the bucket is located. This is the region id for "EU (Frankfurt)"

In our case, we are implementing the crop avatar functionality, for which avatars will be stored on the “avatars-smashing” bucket. However, in our application we may have several other buckets for other functionalities, requiring to execute the same operations of uploading, downloading and listing files. Hence, we implement the common methods on an abstract class AWS_S3, and we obtain the inputs, such as the bucket name defined through function get_bucket, in the implementing child classes.

// Load the SDK and import the AWS objects require 'vendor/autoload.php'; use Aws\S3\S3Client; use Aws\Exception\AwsException; // Definition of an abstract class abstract class AWS_S3 { protected function get_bucket() { // The bucket name will be implemented by the child class return ''; } }

The S3Client class exposes the API for interacting with S3. We instantiate it only when needed (through lazy-initialization), and save a reference to it under $this->s3Client as to keep using the same instance:

abstract class AWS_S3 { // Continued from above... protected $s3Client; protected function get_s3_client() { // Lazy initialization if (!$this->s3Client) { // Create an S3Client. Provide the credentials and region as defined through constants in wp-config.php $this->s3Client = new S3Client([ 'version' => '2006-03-01', 'region' => AWS_REGION, 'credentials' => [ 'key' => AWS_ACCESS_KEY_ID, 'secret' => AWS_SECRET_ACCESS_KEY, ], ]); } return $this->s3Client; } }

When we are dealing with $file in our application, this variable contains the absolute path to the file in disk (e.g. /var/app/current/wp-content/uploads/users/654/leo.jpg), but when uploading the file to S3 we should not store the object under the same path. In particular, we must remove the initial bit concerning the system information (/var/app/current) for security reasons, and optionally we can remove the /wp-content bit (since all files are stored under this folder, this is redundant information), keeping only the relative path to the file (/uploads/users/654/leo.jpg). Conveniently, this can be achieved by removing everything after WP_CONTENT_DIR from the absolute path. Functions get_file and get_file_relative_path below switch between the absolute and the relative file paths:

abstract class AWS_S3 { // Continued from above... function get_file_relative_path($file) { return substr($file, strlen(WP_CONTENT_DIR)); } function get_file($file_relative_path) { return WP_CONTENT_DIR.$file_relative_path; } }

When uploading an object to S3, we can establish who is granted access to the object and the type of access, done through the access control list (ACL) permissions. The most common options are to keep the file private (ACL => “private”) and to make it accessible for reading on the internet (ACL => “public-read”). Because we will need to request the file directly from S3 to show it to the user, we need ACL => “public-read”:

abstract class AWS_S3 { // Continued from above... protected function get_acl() { return 'public-read'; } }

Finally, we implement the methods to upload an object to, and download an object from, the S3 bucket:

abstract class AWS_S3 { // Continued from above... function upload($file) { $s3Client = $this->get_s3_client(); // Upload a file object to S3 $s3Client->putObject([ 'ACL' => $this->get_acl(), 'Bucket' => $this->get_bucket(), 'Key' => $this->get_file_relative_path($file), 'SourceFile' => $file, ]); } function download($file) { $s3Client = $this->get_s3_client(); // Download a file object from S3 $s3Client->getObject([ 'Bucket' => $this->get_bucket(), 'Key' => $this->get_file_relative_path($file), 'SaveAs' => $file, ]); } }

Then, in the implementing child class we define the name of the bucket:

class AvatarCropper_AWS_S3 extends AWS_S3 { protected function get_bucket() { return 'avatars-smashing'; } }

Finally, we simply instantiate the class to upload the avatars to, or download from, S3. In addition, when transitioning from steps 1 to 2 and 2 to 3, we need to communicate the value of $file. We can do this by submitting a field “file_relative_path” with the value of the relative path of $file through a POST operation (we don’t pass the absolute path for security reasons: no need to include the “/var/www/current” information for outsiders to see):

// Step 1: after the file was uploaded to the server, upload it to S3. Here, $file is known $avatarcropper = new AvatarCropper_AWS_S3(); $avatarcropper->upload($file); // Get the file path, and send it to the next step in the POST $file_relative_path = $avatarcropper->get_file_relative_path($file); // ... // -------------------------------------------------- // Step 2: get the $file from the request and download it, manipulate it, and upload it again $avatarcropper = new AvatarCropper_AWS_S3(); $file_relative_path = $_POST['file_relative_path']; $file = $avatarcropper->get_file($file_relative_path); $avatarcropper->download($file); // Do manipulation of the file // ... // Upload the file again to S3 $avatarcropper->upload($file); // -------------------------------------------------- // Step 3: get the $file from the request and download it, and then save it $avatarcropper = new AvatarCropper_AWS_S3(); $file_relative_path = $_REQUEST['file_relative_path']; $file = $avatarcropper->get_file($file_relative_path); $avatarcropper->download($file); // Save it, whatever that means // ... Displaying The File Directly From S3

If we want to display the intermediate state of the file after manipulation on step 2 (e.g. the user avatar after cropped), then we must reference the file directly from S3; the URL couldn’t point to the file on the server since, once again, we don’t know which server will handle that request.

Below, we add function get_file_url($file) which obtains the URL for that file in S3. If using this function, please make sure that the ACL of the uploaded files is “public-read”, or otherwise it won’t be accessible to the user.

abstract class AWS_S3 { // Continue from above... protected function get_bucket_url() { $region = $this->get_region(); // North Virginia region is simply "s3", the others require the region explicitly $prefix = $region == 'us-east-1' ? 's3' : 's3-'.$region; // Use the same scheme as the current request $scheme = is_ssl() ? 'https' : 'http'; // Using the bucket name in path scheme return $scheme.'://'.$prefix.'.amazonaws.com/'.$this->get_bucket(); } function get_file_url($file) { return $this->get_bucket_url().$this->get_file_relative_path($file); } }

Then, we can simply we get the URL of the file on S3 and print the image:

printf( "<img src='%s'>", $avatarcropper->get_file_url($file) ); Listing Files

If in our application we want to allow the user to view all previously uploaded avatars, we can do so. For that, we introduce function get_file_urls which lists the URL for all the files stored under a certain path (in S3 terms, it’s called a prefix):

abstract class AWS_S3 { // Continue from above... function get_file_urls($prefix) { $s3Client = $this->get_s3_client(); $result = $s3Client->listObjects(array( 'Bucket' => $this->get_bucket(), 'Prefix' => $prefix )); $file_urls = array(); if(isset($result['Contents']) && count($result['Contents']) > 0 ) { foreach ($result['Contents'] as $obj) { // Check that Key is a full file path and not just a "directory" if ($obj['Key'] != $prefix) { $file_urls[] = $this->get_bucket_url().$obj['Key']; } } } return $file_urls; } }

Then, if we are storing each avatar under path “/users/${user_id}/“, by passing this prefix we will obtain the list of all files:

$user_id = get_current_user_id(); $prefix = "/users/${user_id}/"; foreach ($avatarcropper->get_file_urls($prefix) as $file_url) { printf( "<img src='%s'>", $file_url ); } Conclusion

In this article, we explored how to employ a cloud object storage solution to act as a common repository to store files for an application deployed on multiple servers. For the solution, we focused on AWS S3, and proceeded to show the steps needed to be integrated into the application: creating the bucket, setting-up the user permissions, and downloading and installing the SDK. Finally, we explained how to avoid security pitfalls in the application, and saw code examples demonstrating how to perform the most basic operations on S3: uploading, downloading and listing files, which barely required a few lines of code each. The simplicity of the solution shows that integrating cloud services into the application is not difficult, and it can also be accomplished by developers who are not much experienced with the cloud.

(rb, ra, yk, il)
Categories: Around The Web

How To Build A Virtual Reality Model With A Real-Time Cross-Device Preview

Smashing Magazine - Tue, 11/06/2018 - 5:50pm
How To Build A Virtual Reality Model With A Real-Time Cross-Device Preview How To Build A Virtual Reality Model With A Real-Time Cross-Device Preview Alvin Wan 2018-11-06T23:50:16+01:00 2018-11-14T13:45:20+00:00

Virtual reality (VR) is an experience based in a computer-generated environment; a number of different VR products make headlines and its applications range far and wide: for the winter Olympics, the US team utilized virtual reality for athletic training; surgeons are experimenting with virtual reality for medical training; and most commonly, virtual reality is being applied to games.

We will focus on the last category of applications and will specifically focus on point-and-click adventure games. Such games are a casual class of games; the goal is to point and click on objects in the scene, to finish a puzzle. In this tutorial, we will build a simple version of such a game but in virtual reality. This serves as an introduction to programming in three dimensions and is a self-contained getting-started guide to deploying a virtual reality model on the web. You will be building with webVR, a framework that gives a dual advantage — users can play your game in VR and users without a VR headset can still play your game on a phone or desktop.

Developing For Virtual Reality

Any developer can create content for VR nowadays. To get a better understanding of VR development, working a demo project can help. Read article →

In the second half of these tutorial, you will then build a “mirror” for your desktop. This means that all movements the player makes on a mobile device will be mirrored in a desktop preview. This allows you see what the player sees, allowing you to provide guidance, record the game, or simply keep guests entertained.

Prerequisites

To get started, you will need the following. For the second half of this tutorial, you will need a Mac OSX. Whereas the code can apply to any platform, the dependency installation instructions below are for Mac.

  • Internet access, specifically to glitch.com;
  • A virtual reality headset (optional, recommended). I use Google Cardboard, which is offered at $15 a piece.

Front-end is messy and complicated these days. That's why we publish articles, printed books and webinars with useful techniques to improve your work. Even better: Smashing Membership with a growing selection of front-end & UX goodies. So you get your work done, better and faster.

Explore Smashing Membership ↬ Step 1: Setting Up A Virtual Reality (VR) Model

In this step, we will set up a website with a single static HTML page. This allows us to code from your desktop and automatically deploy to the web. The deployed website can then be loaded on your mobile phone and placed inside a VR headset. Alternatively, the deployed website can be loaded by a standalone VR headset. Get started by navigating to glitch.com. Then,

  1. Click on “New Project” in the top-right.
  2. Click on “hello-express” in the drop-down.
(Large preview)

Next, click on views/index.html in the left sidebar. We will refer to this as your “editor”.

(Large preview)

To preview the webpage, click on “Preview” in the top left. We will refer to this as your preview. Note that any changes in your editor will be automatically reflected in this preview, barring bugs or unsupported browsers.

(Large preview)

Back in your editor, replace the current HTML with the following boilerplate for a VR model.

<!DOCTYPE html> <html> <head> <script src="https://aframe.io/releases/0.7.0/aframe.min.js"></script> </head> <body> <a-scene> <!-- blue sky --> <a-sky color="#a3d0ed"></a-sky> <!-- camera with wasd and panning controls --> <a-entity camera look-controls wasd-controls position="0 0.5 2" rotation="0 0 0"></a-entity> <!-- brown ground --> <a-box shadow id="ground" shadow="receive:true" color="#847452" width="10" height="0.1" depth="10"></a-box> <!-- start code here --> <!-- end code here --> </a-scene> </body> </html>

Navigate see the following.

(Large preview)

To preview this on your VR headset, use the URL in the omnibar. In the picture above, the URL is https://point-and-click-vr-game.glitch.me/. Your working environment is now set up; feel free to share this URL with family and friends. In the next step, you will create a virtual reality model.

Step 2: Build A Tree Model

You will now create a tree, using primitives from aframe.io. These are standard objects that Aframe has pre-programmed for ease of use. Specifically, Aframe refers to objects as entities. There are three concepts, related to all entities, to organize our discussion around:

  1. Geometry and material,
  2. Transformation Axes,
  3. Relative Transformations.

First, geometry and material are two building blocks of all three-dimensional objects in code. The geometry defines the “shape” — a cube, a sphere, a pyramid, and so on. The material defines static properties of the shape, such as color, reflectiveness, roughness.

Aframe simplifies this concept for us by defining primitives, such as <a-box>, <a-sphere>, <a-cylinder> and many others to make a specification of a geometry and its material simpler. Start by defining a green sphere. On line 19 in your code, right after <!-- start code here -->, add the following.

<!-- start code here --> <a-sphere color="green" radius="0.5"></a-sphere> <!-- new line --> <!-- end code here -->

Second, there are three axes to transform our object along. The x axis runs horizontally, where x values increase as we move right. The y axis runs vertically, where y values increase as we move up. The z axis runs out of your screen, where z values increase as we move towards you. We can translate, rotate, or scale entities along these three axes.

For example, to translate an object “right,” we increase its x value. To spin an object like a top, we rotate it along the y-axis. Modify line 19 to move the sphere “up” — this means you need to increase the sphere’s y value. Note that all transformations are specified as <x> <y> <z>, meaning to increase its y value, you need to increase the second value. By default, all objects are located at position 0, 0, 0. Add the position specification below.

<!-- start code here --> <a-sphere color="green" radius="0.5" position="0 1 0"></a-sphere> <!-- edited line --> <!-- end code here -->

Third, all transformations are relative to its parent. To add a trunk to your tree, add a cylinder inside of the sphere above. This ensures that the position of your trunk is relative to the sphere’s position. In essence, this keeps your tree together as one unit. Add the <a-cylinder> entity between the <a-sphere ...> and </a-sphere> tags.

<a-sphere color="green" radius="0.5" position="0 1 0"> <a-cylinder color="#84651e" position="0 -0.9 0" radius="0.05"></a-cylinder> <!-- new line --> </a-sphere>

To make this treeless barebones, add more foliage, in the form of two more green spheres.

<a-sphere color="green" radius="0.5" position="0 0.75 0"> <a-cylinder color="#84651e" position="0 -0.9 0" radius="0.05"></a-cylinder> <a-sphere color="green" radius="0.35" position="0 0.5 0"></a-sphere> <!-- new line --> <a-sphere color="green" radius="0.2" position="0 0.8 0"></a-sphere> <!-- new line --> </a-sphere>

Navigate back to your preview, and you will see the following tree:

(Large preview)

Reload the website preview on your VR headset, and check out your new tree. In the next section, we will make this tree interactive.

Step 3: Add Click Interaction To Model

To make an entity interactive, you will need to:

  • Add an animation,
  • Have this animation trigger on click.

Since the end user is using a virtual reality headset, clicking is equivalent to staring: in other words, stare at an object to “click” on it. To effect these changes, you will start with the cursor. Redefine the camera, by replacing line 13 with the following.

<a-entity camera look-controls wasd-controls position="0 0.5 2" rotation="0 0 0"> <a-entity cursor="fuse: true; fuseTimeout: 250" position="0 0 -1" geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03" material="color: black; shader: flat" scale="0.5 0.5 0.5" raycaster="far: 20; interval: 1000; objects: .clickable"> <!-- add animation here --> </a-entity> </a-entity>

The above adds a cursor that can trigger the clicking action. Note the objects: .clickable property. This means that all objects with the class “clickable” will trigger the animation and receive a “click” command where appropriate. You will also add an animation to the click cursor, so that users know when the cursor triggers a click. Here, the cursor will shrink slowly when pointing at a clickable object, snapping after a second to denote an object has been clicked. Replace the comment <!-- add animation here --> with the following code:

<a-animation begin="fusing" easing="ease-in" attribute="scale" fill="backwards" from="1 1 1" to="0.2 0.2 0.2" dur="250"></a-animation>

Move the tree to the right by 2 units and add class “clickable” to the tree, by modifying line 29 to match the following.

<a-sphere color="green" radius="0.5" position="2 0.75 0" class="clickable">

Next, you will:

  • Specify an animation,
  • Trigger the animation with a click.

Due to Aframe’s easy-to-use animation entity, both steps can be done in quick succession.

Add an <a-animation> tag on line 33, right after the <a-cylinder> tag but before the end of the </a-sphere>.

<a-animation begin="click" attribute="position" from="2 0.75 0" to="2.2 0.75 0" fill="both" direction="alternate" repeat="1"></a-animation>

The above properties specify a number of configurations for the animation. The animation:

  • Is triggered by the click event
  • Modifies the tree’s position
  • Starts from the original position 2 0.75 0
  • Ends in 2.2 0.75 0 (moving 0.2 units to the right)
  • Animates when traveling to and from the destination
  • Alternates animation between traveling to and from the destination
  • Repeats this animation once. This means the object animates twice in total — once to the destination and once back to the original position.

Finally, navigate to your preview, and drag from the cursor to your tree. Once the black circle rests on the tree, the tree will move to the right and back.

Large preview

This concludes the basics needed to build a point-and-click adventure game, in virtual reality. To view and play a more complete version of this game, see the following short scene. The mission is to open the gate and hide the tree behind the gate, by clicking on various objects in the scene.

(Large preview)

Next, we set up a simple nodeJS server to serve our static demo.

Step 4: Setup NodeJS Server

In this step, we will set up a basic, functional nodeJS server that serves your existing VR model. In the left sidebar of your editor, select package.json.

Start by deleting lines 2-4.

"//1": "describes your app and its dependencies", "//2": "https://docs.npmjs.com/files/package.json", "//3": "updating this file will download and update your packages",

Change the name to mirrorvr.

{ "name": "mirrorvr", // change me "version": "0.0.1", ...

Under dependencies, add socket.io.

"dependencies": { "express": "^4.16.3", "socketio": "^1.0.0", },

Update the repository URL to match your current glitch’s. The example glitch project is named point-and-click-vr-game. Replace that with your glitch project’s name.

"repository": { "url": "https://glitch.com/edit/#!/point-and-click-vr-game" },

Finally, Change the "glitch" tag to "vr".

"keywords": [ "node", "vr", // change me "express" ]

Double check that your package.json now matches the following.

{ "name": "mirrorvr", "version": "0.0.1", "description": "Mirror virtual reality models", "main": "server.js", "scripts": { "start": "node server.js" }, "dependencies": { "express": "^4.16.3", "socketio": "^1.0.0" }, "engines": { "node": "8.x" }, "repository": { "url": "https://glitch.com/edit/#!/point-and-click-vr-game" }, "license": "MIT", "keywords": [ "node", "vr", "express" ] }

Double check that your code from the previous parts matches the following, in views/index.html.

<!DOCTYPE html> <html> <head> <script src="https://aframe.io/releases/0.7.0/aframe.min.js"></script> </head> <body> <a-scene> <!-- blue sky --> <a-sky color="#a3d0ed"></a-sky> <!-- camera with wasd and panning controls --> <a-entity camera look-controls wasd-controls position="0 0.5 2" rotation="0 0 0"> <a-entity cursor="fuse: true; fuseTimeout: 250" position="0 0 -1" geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03" material="color: black; shader: flat" scale="0.5 0.5 0.5" raycaster="far: 20; interval: 1000; objects: .clickable"> <a-animation begin="fusing" easing="ease-in" attribute="scale" fill="backwards" from="1 1 1" to="0.2 0.2 0.2" dur="250"></a-animation> </a-entity> </a-entity> <!-- brown ground --> <a-box shadow id="ground" shadow="receive:true" color="#847452" width="10" height="0.1" depth="10"></a-box> <!-- start code here --> <a-sphere color="green" radius="0.5" position="2 0.75 0" class="clickable"> <a-cylinder color="#84651e" position="0 -0.9 0" radius="0.05"></a-cylinder> <a-sphere color="green" radius="0.35" position="0 0.5 0"></a-sphere> <a-sphere color="green" radius="0.2" position="0 0.8 0"></a-sphere> <a-animation begin="click" attribute="position" from="2 0.75 0" to="2.2 0.75 0" fill="both" direction="alternate" repeat="1"></a-animation> </a-sphere> <!-- end code here --> </a-scene> </body> </html>

Modify the existing server.js.

Start by importing several NodeJS utilities.

  • Express
    This is the web framework we will use to run the server.
  • http
    This allows us to launch a daemon, listening for activity on various ports.
  • socket.io
    The sockets implementation that allows us to communicate between client-side and server-side in nearly real-time.

While importing these utilities, we additionally initialize the ExpressJS application. Note the first two lines are already written for you.

var express = require('express'); var app = express(); /* start new code */ var http = require('http').Server(app); var io = require('socket.io')(http); /* end new code */ // we've started you off with Express,

With the utilities loaded, the provided server next instructs the server to return index.html as the homepage. Note there is no new code written below; this is simply an explanation of the existing source code.

// http://expressjs.com/en/starter/basic-routing.html app.get('/', function(request, response) { response.sendFile(__dirname + '/views/index.html'); });

Finally, the existing source code instructs the application to bind to and listen to a port, which is 3000 by default unless specified otherwise.

// listen for requests :) var listener = app.listen(process.env.PORT, function() { console.log('Your app is listening on port ' + listener.address().port); });

Once you are finished editing, Glitch automatically reloads the server. Click on “Show” in the top-left to preview your application.

Your web application is now up and running. Next, we will send messages from the client to the server.

Step 5: Send Information From Client To Server

In this step, we will use the client to initialize a connection with the server. The client will additionally inform the server if it is a phone or a desktop. To start, import the soon-to-exist Javascript file in your views/index.html.

After line 4, include a new script.

<script src="/client.js" type="text/javascript"></script>

On line 14, add camera-listener to the list of properties for the camera entity.

<a-entity camera-listener camera look-controls...> ... </a-entity>

Then, navigate to public/client.js in the left sidebar. Delete all Javascript code in this file. Then, define a utility function that checks if the client is a mobile device.

/** * Check if client is on mobile */ function mobilecheck() { var check = false; (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera); return check; };

Next, we will define a series of initial messages to exchange with the server side. Define a new socket.io object to represent the client’s connection to the server. Once the socket connects, log a message to the console.

var socket = io(); socket.on('connect', function() { console.log(' * Connection established'); });

Check if the device is mobile, and send corresponding information to the server, using the function emit.

if (mobilecheck()) { socket.emit('newHost'); } else { socket.emit('newMirror'); }

This concludes the client’s message sending. Now, amend the server code to receive this message and react appropriately. Open the server server.js file.

Handle new connections, and immediately listen for the type of client. At the end of the file, add the following.

/** * Handle socket interactions */ io.on('connection', function(socket) { socket.on('newMirror', function() { console.log(" * Participant registered as 'mirror'") }); socket.on('newHost', function() { console.log(" * Participant registered as 'host'"); }); });

Again, preview the application by clicking on “Show” in the top left. Load that same URL on your mobile device. In your terminal, you will see the following.

listening on *: 3000 * Participant registered as 'host' * Participant registered as 'mirror'

This is the first of simple message-passing, where our client sends information back to the server. Quit the running NodeJS process. For the final part of this step, we will have the client send camera information back to the server. Open public/client.js.

At the very end of the file, include the following.

var camera; if (mobilecheck()) { AFRAME.registerComponent('camera-listener', { tick: function () { camera = this.el.sceneEl.camera.el; var position = camera.getAttribute('position'); var rotation = camera.getAttribute('rotation'); socket.emit('onMove', { "position": position, "rotation": rotation }); } }); }

Save and close. Open your server file server.js to listen for this onMove event.

Add the following, in the newHost block of your socket code.

socket.on('newHost', function() { console.log(" * Participant registered as 'host'"); /* start new code */ socket.on('onMove', function(data) { console.log(data); }); /* end new code */ });

Once again, load the preview on your desktop and on your mobile device. Once a mobile client is connected, the server will immediately begin logging camera position and rotation information, sent from the client to the server. Next, you will implement the reverse, where you send information from the server back to the client.

Step 6: Send Information From Server To Client

In this step, you will send a host’s camera information to all mirrors. Open your main server file, server.js.

Change the onMove event handler to the following:

socket.on('onMove', function(data) { console.log(data); // delete me socket.broadcast.emit('move', data) });

The broadcast modifier ensures that the server sends this information to all clients connected to the socket, except for the original sender. Once this information is sent to a client, you then need to set the mirror’s camera accordingly. Open the client script, public/client.js.

Here, check if the client is a desktop. If so, receive the move data and log accordingly.

if (!mobilecheck()) { socket.on('move', function(data) { console.log(data); }); }

Load the preview on your desktop and on your mobile device. In your desktop browser, open the developer console. Then, load the app on your mobile phone. As soon as the mobile phone loads the app, the developer console on your desktop should light up with camera position and rotation.

Open the client script once more, at public/client.js. We finally adjust the client camera depending on the information sent.

Amend the event handler above for the move event.

socket.on('move', function(data) { /* start new code */ camera.setAttribute('rotation', data["rotation"]); camera.setAttribute('position', data["position"]); /* end new code */ });

Load the app on your desktop and your phone. Every movement of your phone is reflected in the corresponding mirror on your desktop! This concludes the mirror portion of your application. As a desktop user, you can now preview what your mobile user sees. The concepts introduced in this section will be crucial for further development of this game, as we transform a single-player to a multiplayer game.

Conclusion

In this tutorial, we programmed three-dimensional objects and added simple interactions to these objects. Additionally, you built a simple message passing system between clients and servers, to effect a desktop preview of what your mobile users see.

These concepts extend beyond even webVR, as the notion of a geometry and material extend to SceneKit on iOS (which is related to ARKit), Three.js (the backbone for Aframe), and other three-dimensional libraries. These simple building blocks put together allow us ample flexibility in creating a fully-fledged point-and-click adventure game. More importantly, they allow us to create any game with a click-based interface.

Here are several resources and examples to further explore:

  • MirrorVR
    A fully-fledged implementation of the live preview built above. With just a single Javascript link, add a live preview of any virtual reality model on mobile to a desktop.
  • Bit by Bit
    A gallery of kids’ drawings and each drawing’s corresponding virtual reality model.
  • Aframe
    Examples, developer documentation, and more resources for virtual reality development.
  • Google Cardboard Experiences
    Experiences for the classroom with custom tools for educators.

Next time, we will build a complete game, using web sockets to facilitate real-time communication between players in a virtual reality game. Feel free to share your own models in the comments below.

(rb, ra, yk, il)
Categories: Around The Web

The Value Of Storyboarding For Product Design

Smashing Magazine - Tue, 11/06/2018 - 8:15am
The Value Of Storyboarding For Product Design The Value Of Storyboarding For Product Design Joshua Bumcrot 2018-11-06T14:15:20+01:00 2018-11-14T13:45:20+00:00

When you think of the word “storyboarding,” you probably think about film, media, and video creation. Historically, this is what storyboards have been used for, but who says we can’t use them for product development as well?

Storyboards are an effective communication and product development tool for digital marketers, content creators, user experience specialists, and product managers. In this article, I’ll teach you why storyboarding is valuable to the product development process, as well as why it can reduce interdepartmental miscommunications as well as overhead costs.

Below is a comic of a clear and common situation experienced by most companies. Words can only go so far as to effective explanations and miscommunications are are a common occurrence in the product development world. As you read the article, keep this image in mind and notice examples where storyboarding or visual communication may be able to prevent misunderstandings.

The ‘Tree Swing’ communication problem (Large preview)

Web forms are such an important part of the web, but we design them poorly all the time. The brand-new “Form Design Patterns” book is our new practical guide for people who design, prototype and build all sorts of forms for digital services, products and websites. The eBook is free for Smashing Members.

Check the table of contents ↬

I’m going to set a scene that almost all tech companies experience on a regular basis:

Marketer Mary has an idea for a new product. Mary thinks this product will be a huge winner and goes to tech to have them build it.

Developer Diane hears Mary’s idea and tells her she will start working on it. As Diane continues to build out functionality, she realizes that the product will actually be much more efficient if she combines some of the parts and cuts out others. Diane finishes building the product and sends it over to Creative Chris.

Chris meets with Mary to discuss the product’s UI, where Mary explains her ideas. Chris gets to work. He starts to incorporate Mary’s ideas but quickly realizes that he has a much better and more interesting UI concept in mind, and eventually completes the product that way.

Once the project is complete, Mary is upset. The product has neither the functionality she asked for, nor does the UI allow the user to navigate the product properly.

All three departments go back to the drawing board to redo the product, and a significant amount of time and money has been wasted.

This common situation is not one person’s fault. Mary should have explained to Diane and Chris her clear end goal of the product and let them create it with the goal in mind. Diane should have understood that Mary was asking for specific functionalities for a reason and even though her product may have been more efficient, it’s useless if it doesn’t accomplish the desired end goal. Chris should have understood that even though Mary’s UI concept may not have been the most aesthetically pleasing, she had that UI for a reason. They should have worked together to create a concept that works for everyone.

There are a few ideas out there on how to attack common product development miscommunication problems, however, many have found the most successful solution is to create storyboards throughout the product development process.

A storyboard is a collection of cells, either in a linear progression or mapped out from a central idea that tells a story. Each cell can contain an image, a title, and a description that provides specific information to the reader about certain aspects of the story. Storyboards are meant to be simple representations of a larger concept, and force both its creators and readers to break down large complex topics into simple step by step subsections.

The Journey Of Entrepreneur Erin

The fictional, but based on real events journey of an entrepreneur using storyboards to help her and her team design, build, and iterate a her product.

Erin left her job at the big company she worked for and decided to pursue her dreams and start her own company. She already had an idea — SoLoMoFoo. SoLoMoFoo is an application to alert employees when free food is available in common areas — like conference rooms, shared office kitchens, or private offices. At her old job, she had noticed far too often that free food goes to waste due to lack of awareness and employees become disgruntled after hearing about free food that they had just missed. She decided that this problem needed a solution and that she was going to build it! First, she needed to figure out who exactly her target users would be.

A useful way to discover potential target users is to create personas. Erin decided she was going to map out a few of her target users and buyers and record some of their unique characteristics.

Creating Personas Creating personas can help you step out of yourself. It can help you to recognize that different people have different needs and expectations, and it can also help you to identify with the user you’re designing for.

— Rikke Dam, Co-Founder of Interaction Design Foundation Personas for SoLoMoFoo (Large preview)

Erin knows that SoLoMoFoo will solve a problem that exists (the lack of awareness about free available food) — but who does this problem exist for? Who will be using her product? Before Erin starts to create storyboards she first has to build her personas. Generally, companies will have to focus on two different types of personas — user personas and buyer personas.

1. User Personas

These are fictional depictions of quintessential users fitting a certain criteria. Most products will try to limit the number of two or a maximum of three key user personas, and then focus the majority of marketing efforts on attracting these users. In SoLoMoFoo’s case, there are two key user personas that Erin has identified:

  • Baking Ben
    Ben is often bringing free food into the workplace to share with coworkers. He feels a little weird about emailing the entire office every time he brings in cupcakes so he would love an app that alerts his coworkers for him.
  • Hangry Hank
    Hank is constantly missing free food and is upset because of it. He feels less productive when he’s hungry and would be very interested in an app that alerts him anytime food is available.
2. Buyer Personas

Often times the intended user of your product is not the same as the intended buyer. In SoLoMoFoo’s business model, an entire company will purchase the SoLoMoFoo app and have their employees download it. This way everyone in the office will both be able to send alerts when they have free food, and receive alerts when they’re looking for food. Erin has decided that the most likely purchaser of SoLoMoFoo will be the HR department.

  • HR Hailey
    Hailey is the HR manager and has purchasing power. She is constantly looking for ways to improve employees’ morale and engagement. She is incentivized by her superiors to inspire energy and teamwork amongst the employees and has a budget to spend on applications or tools that will help her do this.

Creating these personas will help you step inside the shoes of both your users, and your buyers (if they are different). It helps you take a step back from your product and see it through the eyes of the people you are designing for.

To start creating the personas you need, there are several online resources you can use. For example, you can use a persona worksheet template like one of these:

HubSpot persona template (Large preview) Xtensio persona template (Credit to Xtensio) (Large preview)

Or use a persona creation tool like this one from HubSpot.

After personas have been created, the next step is to discover how these personas would come in contact with the problem you're solving for, your product as a solution, and how the product would ultimately benefit their lives. A great way to step into your personas’ shoes is to create journey maps.

Journey Mapping Customer journey mapping helps you to visualize your customer’s experience from the customer’s point of view, across all the different touchpoints they have with your brand as they seek to achieve a specific goal or goals.

— Tandem Seven Experts

Now that Erin has identified SoLoMoFoo’s key user and buyer personas it’s time for her to discover how these personas may come across her product, how they would use it, and what potential obstacles they may face throughout the process. A great way to do this is to create journey maps in the form of storyboards. Creating these journey mapping storyboards forces the product designer to walk in the user or buyers shoes and experience their product in a step-by-step manner.

Customer journey map for SoLoMoFoo (Large preview)

A journey map storyboard can generally be broken down in six key components:

  1. Problem Experienced
    You have decided to create your product for a reason. You believe that your target users are experiencing a problem that they want solved. What is the problem that your product solves for?
  2. Solution Search
    After your persona has experienced the problem, you believe they will go searching for a solution. What methods will they use to search? These can be potential marketing channels to consider for your go-to-market strategy.
  3. Product Discovery
    During their search, your personas will come across your product and decide to start using it. How will they know this is the product for them? How will they get started? What barriers to entry might they face?
  4. Product Experienced
    The persona will now use the product and experience the intended objective. How do they use it? Can they use it immediately or are there other steps they need to take?
  5. Problem Alleviated
    After the intended product objective is attained the users problem is alleviated. Is this the same problem you were attempting to solve for at the beginning of your storyboard? What other potential problems might stem from your solution?
  6. Beneficial Outcome
    Now that persona’s problem is alleviate, why is their life better? What benefit did solving their problem bring to them and how will this improve their situation?

Need an example? Take a look at the illustration below:

Customer journey map template (Large preview)

In the case of Entrepreneur Erin, she has to consider how the lack of access to free food would affect HR Hailey, how she would research possible solutions, how she would come across SoLoMoFoo, how the SoLoMoFoo platform would be implemented at her place of work, and the potential benefits and timeline for those benefits that the SoLoMoFoo program would bring.

Buyer journey map for SoLoMoFoo (Large preview)

Creating journey maps in the form of storyboards is a useful way to humanize your buyers. It’s essential to remember that your users are not just numbers, but real people. Having a human character and their personal journey map story associated with each persona serves as a constant reminder that your users are people, and their needs are constantly changing.

These journey maps are not only a great external product development tool, but also a great way to minimize internal miscommunications.

By developing user personas and a customer journey map storyboard, everyone is able to visualize the steps a persona would take when engaging with your product. Creating journey maps and presenting them to coworkers allows team members to view your vision in a realistic and tangible way. Once every department understands your journey maps, you can all reach consensus on what the final product will be, and the development process can continue with everyone on the same page.

Storyboarding For UX Design Storyboarding in UX is a tool which can help you visually predict and explore a user’s experience with a product.

— Nick Babich, Editor-in-chief of UX Planet User-experience storyboard (Large preview)

Entrepreneur Erin has successfully identified who she believes her target users and buyers are and how they will come in contact and use her product. Now it’s time for her to design the SoLoMoFoo user experience.

A useful way to step back and view your product from a user’s point of view is to create UX concepts through storyboarding. Creating storyboards, cell-by-cell, forces you to walk through every step of your UX process as a user. You can easily create multiple storyboards and try out different UX approaches to find the most efficient concept.

Erin wants to design SoLoMoFoo as a simple app so she reviews the personas and journey maps with her development and marketing team so they are clear on the product vision and together they start designing a user experience.

Creating these visual, tangible storyboards points out potential flaws in your UX — maybe you are forcing your users to take a long leap in one step and is not intuitive. Or, perhaps you have a few steps in your UX that could be combined into one thus eliminating superfluous actions.

According to UX Specialist, Luca Morovian:

“The UX storyboard can help visually predict and explore the user experience with a product. It visualizes how people would interact with a service or app. A UX storyboard can also help understand users current motivations and experiences connected to a certain problem.”

The power and value of storyboarding for UX comes in the creation process, letting you experience your product as a user, which allows you to best optimize for effective design and an improved conversion rate.

User-experience storyboard with detailed descriptions (Large preview)

Finally, Erin has identified her target users and buyers, journey mapped their process, and built a streamlined UX — but the product development process is never over.

Storyboarding For Product Iterations And Improvements Your people know your products better than anyone else so as long as you ask the right questions and use appealing storyboards, you can solicit reactions from them and start a discussion on whatever it is you need to find out.

— Andre Bourque, Entrepreneur

Erin has now identified who she thinks will use and buy her SoLoMoFoo, how they will come across the product and engage with it, and how the platform user flow will be designed.

As product developers are aware, a product is never fully complete. As technology changes, users adapt and so must your product. It’s important to constantly be referring back to your customer journey maps and user personas to make sure they are still accurate to your product. As you learn more about your users and user-case scenarios, your product should adapt accordingly.

When looking into product iterations, most product development teams have a tough time isolating the scope and deciding on which aspect of the current product they want to change. Storyboards can help product designers break their product into individual segments, which then allows them to specifically work on one aspect of a product without involving others.

Here are a few questions you may want to use as a guide:

  • Do you have a homepage conversion rate issue and want to modernize UI?
  • Are users not responding to your call-to-actions? Maybe your UX is too complicated? Or maybe people just aren’t purchasing your product, so are you really solving your perceived problem?

Having a storyboard of your product allows you to clearly see which parts of your product are responsible for which users’ actions. As a result, you can focus your iteration process on one aspect of your product instead of the product as a whole allowing for faster improvements and a cleaner process.

How To Begin

Now that you have the knowledge on how to start using storyboards in your product development process, it’s time to get started.

Here are some quick steps to help you make the first step:

  1. Identify the problem your product is solving for;
  2. Identify 1-3 user personas and 1-3 buyer personas (if different);
  3. Create journey maps for your personas;
  4. Design UX flows around your target audiences’ needs;
  5. Iterate, repeat, and improve!

In conclusion, you now have tools to start incorporating storyboarding into your product development process. From the beginning in identifying your target users all the way to the end in building a UX and iterating your product for improvements.

Using storyboarding through the product design process will help prevent simple miscommunications and allow both you and your team to have a clear concept of what your product will accomplish and look like. Start storyboarding out your new product idea today!

(yk, ra, il)
Categories: Around The Web

Improve Animated GIF Performance With HTML5 Video

Smashing Magazine - Mon, 11/05/2018 - 8:30am
Improve Animated GIF Performance With HTML5 Video Improve Animated GIF Performance With HTML5 Video Ayo Isaiah 2018-11-05T14:30:14+01:00 2018-11-14T13:45:20+00:00

It has been brought to our attention that this piece is a reworded version of an article published on the Google Web Fundamentals site, written by our friend Jeremy Wagner. The original piece can be found here, and we would recommend it to you.

I’d like to personally apologize to Jeremy for not identifying this as a copy of his work. Recognizing and crediting the work of people in our community is something I — and the whole team — care very much about. I’m sorry that we failed to do the right thing this time.

— Rachel Andrew, on behalf of the Editorial Team

Animated GIFs have a lot going for them; they’re easy to make and work well enough in literally all browsers. But the GIF format was not originally intended for animation. The original design of the GIF format was to provide a way to compress multiple images inside a single file using a lossless compression algorithm (called LZW compression) which meant they could be downloaded in a reasonably short space of time, even on slow connections.

Later, basic animation capabilities were added which allowed the various images (frames) in the file to be painted with time delays. By default, the series of frames that constitute the animation was displayed only once, stopping after the last frame was shown. Netscape Navigator 2.0 was the first browser to added the ability for animated GIFs to loop, which lead to the rise of animated GIFs as we know them today.

As an animation platform, the GIF format is incredibly limited. Each frame in the animation is restricted to a palette of just 256 colors, and over the years, advances in compression technology has made leading to several improvements the way animations and video files are compressed and used. Unlike proper video formats, the GIF format does not take advantage of any of the new technology meaning that even a few seconds of content can lead to tremendously large file sizes since a lot of repetitive information is stored.

Even if you try to tweak the quality and length of a GIF with a tool like Gifsicle, it can be difficult to cut it down to a reasonable file size. This is the reason why GIF heavy websites like Giphy, Imgur and the likes do not use the actual GIF format, but rather convert it to HTML5 video and serve those to users instead. As the Pinterest Engineering team found, converting animated GIFs to video can decrease load times and improve playback smoothness leading to a more pleasant user experience.

Hence, we’re going to look at some techniques that enable us use HTML5 video as a drop in replacement for animated GIFs. We’ll learn how to convert animated GIFs to video files and examine how to properly embed these video files on the web so that they act just like a GIF would. Finally, we’ll consider a few potential drawbacks that you need to ponder before using this solution.

Convert Animated GIFs To Video

The first step is to convert GIF files to a video format. MP4 is the most widely supported format in browsers with almost 94% of all browsers enjoying support, so that’s a safe default.

94% of all browsers support the MP4 format (Large preview)

Another option is the WebM format which offers high quality videos, often comparable to an MP4, but usually at a reduced file size. However, at this time, browser support is not as widespread so you can’t just go replacing MP4 files with their WebM equivalents.

Internet Explorer and Safari are notable browsers without WebM support (Large preview)

However, because the <video> tag supports multiple <source> files, we can serve WebM videos to browsers that support them while falling back to MP4 everywhere else.

Let’s go ahead and convert an animated GIF to both MP4 and WebM. There are several online tools that can help you do this, but many of them use ffmpeg under the hood so we’ll skip the middle man and just use that instead. ffmpeg is a free and open source command line tool that is designed for the processing of video and audio files. It can also be used to convert an animated GIF to video formats.

To find out if you have ffmpeg on your machine, fire up a terminal and run the ffmpeg command. This should display some diagnostic information, otherwise, you’ll need to install it. Installation instructions for Windows, macOS and Linux can be found on this page. Since we’ll be converting to is WebM, you need to make sure that whatever ffmpeg build you install is compiled with libvpx.

To follow along with the commands that are included in this article, you can use any animated GIF file lying around on your computer or grab this one which is just over 28MB. Let’s begin by converting a GIF to MP4 in the next section.

Convert GIF To MP4

Open up a terminal instance and navigate to the directory where the test gif is located then run the command below to convert it to an MP4 video file:

ffmpeg -i animated.gif video.mp4

This should output a new video file in the current directory after a few seconds depending on the size of the GIF file you’re converting. The -i flag specifies the path to the input GIF file and the output file is specified afterwards (video.mp4 in this instance). Running this command on my 28MB GIF produces an MP4 file that is just 536KB in size, a 98% reduction in file size with roughly the same visual quality.

But we can go even further than that. ffmpeg has so many options that you can use to regulate the video output even further. One way is to employ an encoding method known as Constant Rate Factor (CRF) to trim the size of the MP4 output even further. Here’s the command you need to run:

ffmpeg -i animated.gif -b:v 0 -crf 25 video.mp4

As you can see, there are a couple of new flags in above command compared to the previous one. -b:v is normally used to limit the output bitrate, but when using CRF mode, it must be set to 0. The -crf flag controls the quality of the video output. It accepts a value between 0 and 51; the lower the value, the higher the video quality and file size.

Running the above command on the test GIF, trims down the video output to just 386KB with no discernable difference in quality. If you want to trim the size even further, you could increase the CRF value. Just keep in mind that higher values will lower the quality of the video file.

Convert GIF To WebM

You can convert your GIF file to WebM by running the command below in the terminal:

ffmpeg -i animated.gif -c vp9 -b:v 0 -crf 41 video.webm

This command is almost the same as the previous one, with the exception of a new -c flag which is used to specify the codec that should be used for this conversion. We are using the vp9 codec which succeeds the vp8 codec.

In addition, I’ve adjusted the CRF value to 41 in this case since CRF values don’t necessarily yield the same quality across video formats. This particular value results in a WebM file that is 16KB smaller than the MP4 with roughly the same visual quality.

Now that we know how to convert animated GIFs to video files, let’s look at how we can imitate their behavior in the browser with the HTML5 <video> tag.

Replace Animated GIFs With Video In The Browser

Making a video act like a GIF on a webpage is not as easy as dropping the file in an <img> tag, but it’s not so difficult either. The major qualities of animated GIFs to keep in mind are as follows:

  • They play automatically
  • They loop continuously
  • They are silent

While you get these qualities by default with GIF files, we can cause a video file to act the exact same way using a handful of attributes. Here’s how you’ll embed a video file to behave like a GIF:

<video autoplay loop muted playsinline src="video.mp4"></video>

This markup instructs the browser to automatically start the video, loop it continuously, play no sound, and play inline without displaying any video controls. This gives the same experience as an animated GIF but with better performance.

To specify more that once source for a video, you can use the <source> element within the <video> tag like this:

<video autoplay loop muted playsinline> <source src="video.webm" type="video/webm"> <source src="video.mp4" type="video/mp4"> </video>

This tells the browser to choose from the provided video files depending on format support. In this case, the WebM video will be downloaded and played if it’s supported, otherwise the MP4 file is used instead.

To make this more robust for older browsers which do not support HTML5 video, you could add some HTML content linking to the original GIF file as a fallback.

<video autoplay loop muted playsinline> <source src="video.webm" type="video/webm"> <source src="video.mp4" type="video/mp4"> Your browser does not support HTML5 video. <a href="/animated.gif">Click here to view original GIF</a> </video>

Or you could just add the GIF file directly in an <img> tag:

<video autoplay loop muted playsinline> <source src="video.webm" type="video/webm"> <source src="video.mp4" type="video/mp4"> <img src="animated.gif"> </video>

Now that we’ve examined how to emulate animated GIFs in the browser with HTML5 video, let’s consider a few potential drawbacks to doing so in the next section.

Potential Drawbacks

There are a couple of drawbacks you need to consider before adopting HTML5 video as a GIF replacement. It’s clearly not as convenient as simply uploading a GIF to a page and watch it just work everywhere. You need to encode it first, and it may be difficult to implement an automated solution that works well in all scenarios.

The safest thing would be to convert each GIF manually and check the result of the output to ensure a good balance between visual quality and file size. But on large projects, this may not be practical. In that case, it may be better to look to a service like Cloudinary to do the heavy lifting for you.

Another problem is that unlike images, browsers do not preload video content. Because video files can be of any length, they’re often skipped until the main thread is ready to parse their content. This could delay the loading of a video file by several hundreds of milliseconds.

Additionally, there are quite a few restrictions on autoplaying videos especially on mobile. The muted attribute is actually required for videos to autoplay in Chrome for Android and iOS Safari even if the video does not contain an audio track, and where autoplay is disallowed, the user will only see a blank space where the video should have been. An example is Data Saver mode in Chrome for Android where autoplaying videos will not work even if you set up everything correctly.

To account for any of these scenarios, you should consider setting a placeholder image for the video using the poster attribute so that the video area is still populated with meaningful content if the video does not autoplay for some reason. Also consider using the controls attribute which allows the user to initiate playback even if video autoplay is disallowed.

Wrap Up

By replacing animated GIFs with HTML5 video, we can provide awesome GIF-like experiences without the performance and quality drawbacks associated with GIF files. Doing away with animated GIFs is worth serious consideration especially if your site is GIF-heavy.

There are websites already doing this:

Taking the time to convert the GIF files on your site to video can lead to a massive improvement in page load times. Provided your website is not too complex, it is fairly easy to implement and you can be up and running within a very short amount of time.

(ra,dm)
Categories: Around The Web

SmashingConf New York 2018: That’s What It Was Like

Smashing Magazine - Fri, 11/02/2018 - 7:00am
SmashingConf New York 2018: That’s What It Was Like SmashingConf New York 2018: That’s What It Was Like Bruce Lawson 2018-11-02T14:00:00+02:00 2018-11-14T13:45:20+00:00

As you may know, Smashing Magazine runs a conference — four a year, in fact. Last week saw me, Vitaly, Amanda and Mariona from the Smashing Team in New York, joined by our friend from Beyond Tellerrand Marc Thiele, our amazing DJ, Tobi Lessnow, who wowed the crowd with his ‘sketchnotes to music’. And, of course, there was a full house of our Smashing Family from around the world: the Arctic and Antarctic were the only continents unrepresented.

Pins show where Smashing Conf attendees come from.

Although I’ve spoken at many Smashing Conferences, and attended even more, this was the first time as a member of the team. So I worked the Smashing booth with our amazing volunteer Scott Whitehead so I could meet attendees, and find out what they do, what they’re interested in and what drives them.

I didn’t attend all the talks, as there were many conversations to be had at the booth — but as usual, the audience collaborated on note-taking; here are the notes from Day 1 and notes from Day 2. And, of course, the videos are all online. Smashing Members got early access (as well as other benefits, such as a free monthly webinar and access to e-books, from USD $3 a month).

Our lovely Smashing audience enjoying the talks.

I was struck by how friendly the audience were, to conference staff, speakers and each other. I overheard strangers forming little huddles at our booth and giving each other career and technical advice, and during the breaks people were lining up to ask questions or simply chat with the speakers.

At Smashing Conferences, we don’t big up speakers to be Idols On A Pedestal — they’re developers just like the audience, who happen to have solved a problem that we think others face, so share that knowledge. We even managed an impromptu book signing session, as one of the speakers, Chiara Aliotta, designed the cover and illustrations for Smashing Book 6.

Chiara shows off her work for the cover of Smashing Book 6

It was great fun to meet so many passionate web professionals from all around the globe, some old hands and many just beginning their careers. Thank you for being there, thanks for supporting us, and thanks for buying all our books so I didn’t have to carry them home!

Conference Sketchnotes

We were blessed with having Gary Shroeder make live sketchnotes during the conference, and here are some of them:

Sketchnotes from Dan Mall’s talk. Image credit: Gary Schroeder Sketchnotes from Debbie Millman’s talk. Image credit: Gary Schroeder Sketchnotes from Josh Clark’s talk. Image credit: Gary Schroeder Sketchnotes from Paul Boag’s talk. Image credit: Gary Schroeder

You can also find a lot more sketchnotes on Twitter.

Conference Videos

Linked below are some of the videos recorded at Smashing Conf NY. If you’d like to be in the room with speakers like these next year, take a look at what we have planned for 2019!

Our Upcoming Conferences in 2019

Smashing Conferences are friendly, inclusive events for people who care about their work. No fluff, no fillers, no multi-track experience — just actionable insights applicable to your work right away. With live interactive sessions, showing how we all can better design and build for the web. Here’s the schedule for the next year:

Categories: Around The Web

The 101 Course On Crafting 404 Pages

Smashing Magazine - Thu, 11/01/2018 - 4:48am
The 101 Course On Crafting 404 Pages The 101 Course On Crafting 404 Pages Shelby Rogers 2018-11-01T09:48:57+00:00 2018-11-14T13:45:20+00:00

A lot of people toss around the phrase, “It’s not about the destination. It’s about the journey.” And those people are telling the truth — until they hit a roadblock.

Missed turns or poorly-given directions can cost someone hours on a trip. When you’re on a mission, those hours spent trying to find what you need could ruin the entire experience.

It doesn’t always have to end in disaster. This more optimal scenario could occur: you take a wrong turn, but after stopping at a nearby gas station, you leave with more than accurate directions to your final destination. You’ve also managed to score a free ice cream cone from the sweet old lady working behind the gas station’s register because she saw you were lost… and wanted to cheer you up.

Often, website visitors can wind up getting turned around. It’s not always their fault. They could’ve typed in the wrong URL (common) or clicked on a broken link (our mistake). Whatever the reasoning, you now have confused people who only wanted to engage with your website in some way and now can’t. You hold the reins on their navigation. You can guide them back to where you wanted them to go all along or you can leave them frustrated and in the dark. Do they need to make a U-turn? Did they get off at the wrong exit? Only you can tell them, and the best way to do so is through a 404 error page.

Your website’s 404 error page can deliver either of these scenarios with regard to getting your visitors back on their buyer’s journey. A lackluster 404 page irritates your visitors and chases them away into the hands of a competing website that better guides them to what they’re looking for. That lackluster 404 page has bland messaging with minimal visual elements. It might include a variation of the same serif text: “This page does not exist.” That’s like your web users asking you for directions and telling them nothing more than “well, what you’re looking for isn’t here. Good luck.” Nothing more.

Even brands with seemingly clever branding can neglect a 404 page! The owner of this sad excuse for an error page will remain anonymous (but it rhymes with Bards Tragainst Bubanity). (Large preview)

Unfortunately, even some of the world’s best brands use these 404 pages. No navigation. No interesting text. Nothing that reflects their brand messaging. Visitors are left even more disappointed in their encounter than before.

However, there are some 404 pages that go above and beyond. Rather than the stark white of a standard 404 error page, these pages take an opportunity to speak to users in a more personal tone. Excellent 404 pages are exactly like getting an unexpected treat from a friendly face. Well-crafted 404 Pages can redirect your pages’ visitors away from being lost and confused and to a much happier mood and onto a more helpful page on your website.

Web forms are such an important part of the web, but we design them poorly all the time. The brand new Form Design Patterns book is our new practical guide for people who design, prototype and build all sorts of forms for digital services, products and websites. The eBook is free for Smashing Members.

Check the table of contents ↬

Take Amazon, for instance. On Amazon Day 2018, Amazon learned firsthand the importance of a decent 404 page. Sure, buyers were still frustrated upon reaching a 404 page — even if it included a puppy. However, could you imagine how much more irritated buyers would’ve been had the 404 page looked clinical, cold, and not helpful?

Regardless of what tone you want to take or what visuals you want to use or what copy will best engage your readers, a great 404 page does one thing above all else: Makes website visitors okay with not finding what they need — if only for a moment — and directs them to where they need to go.

While 404 pages vary greatly, the best ones all seem to do two things well:

  1. Support the company’s overall brand and messaging;
  2. Successfully redirect website visitors elsewhere on the page through clear navigation and support.

Thematically, there are a few ways to accomplish the ‘perfect’ 404 page:

1. Nail Down The Overall Tone

If content isn’t your brand’s strong suit, this could be a struggle. However, if you have a sense of your brand’s voice and messaging, you can quickly identify where you can offer something unexpected. Visitors are already going to be disappointed when they hit your 404 page; they’re not getting what they wanted. Your 404 page is an opportunity to show that your brand has humans behind its marketing rather than robotic, cold, automated messages seen elsewhere. In short, move beyond the “this page is unavailable” and its variants.

Regardless of the tone, good 404 pages work like magicians. The best illusionists often acknowledge they’re magicians; they don’t pretend to be something they’re not. 404 pages own up to being an error page; the copy and visuals often reflect that. And then, like any good magician, 404 pages pull the attention away from the problem and put that attention elsewhere. Typically, that’s done with copy that matches the visual elements.

Here are some themes/moods that successful 404 pages have leveraged in the past used to succeed.

Crack A Joke

A joke (even a corny one) can do wonders for alleviating awkwardness or inconvenience. However, unless your brand is built on crude humor (i.e. Cards Against Humanity which ironically doesn’t have a good 404 page), it’s best to make the jokes either tongue in cheek or punny rather than too crass. This example from Modcloth makes a quick pun but keeps the mood light.

Happy and snappy, this 404 page aligns with the rest of the brand’s fun copy. (Large preview) Get Clever

It might not be outright funny, but it’s something that gets a visitor’s attention shortly after arriving on your page. It can be a little sassy, snarky, even unexpected. This 404 page from Blizzard Entertainment does a great job at flipping the script both with its visual tone and its copy.

Sarcasm pays off well for the gaming giant’s 404 page. (Large preview) Be Friendly

Prime example would be LEGO Shop’s 404 page with a friendly customer service rep (albeit a LEGO rep). The friendliness can come from an inviting design or warm copy. Ultimately, it’s anything that culminates in a sense of “oh hey, we’re really sorry about that. Let us try to fix it.”

“If your company’s brand excels in customer service and customer care, maybe taking a tone of genuine friendliness would be most appropriate to carry over brand messaging. If that’s the case, treat your 404 page like an extension of your guest services window.”

(Large preview) Integrate Interactivity

People love to click on things, especially if they’re engaging with the 404 page on desktop. And if they’re engaging with your website, all the better! One of the best examples online of interactivity on a 404 page is from Kualo. The site hosting provider gamified its 404 page into a recreation of Space Invaders, complete with the ability to earn extra lives as you level up. Even more impressive is that Kualo actually offers discounts on its hosting for certain thresholds of points that users reach.

The gamification of Kualo’s 404 keeps users coming back for more chances to win. (Large preview) Be Thought-provoking

Yes, your 404 pages can even be educational! 404 pages can offer up resources and links to other helpful spots on your website. It’s an unexpected distraction that could easily keep guests entertained by more information. National Public Radio (NPR) does this exceptionally well. The media outlet provides a collection of features with one major similarity: the stories are about things which have also disappeared.

(Large preview) Topical/pop-culture Based

Use this one with caution, as there’s a very good chance you’ll have to change your 404 message if you’re going to be topical. Pop culture references move fast; if you’re not careful, you’ve spent too much time developing a 404 page that will be irrelevant in two weeks. (And this is a cardinal sin for any organization with a target market of Millennials or younger.) The Spotify 404 page above recently underwent a shift to keep up with trends. Prior to doing a quick play on Kanye West’s “808 and Heartbreak,” the 404 page featured lyrics from Justin Bieber’s “Sorry.”

(Large preview) 2. Craft Visual Elements To Match That Tone

Once you have an idea of the proper tone for your 404 page, visuals are an important next step in the process. Visuals are often the first element of a 404 page people will note first — and thus, it’s the first representation of that page’s desired tone.

Static visuals help emphasize the page copy. Adding in light animation can often collaborate with the text to further a message or tone. For example, Imgur’s 404 page brings its illustrations to life by making the eyes of its characters follow a visitor’s cursor.

(Large preview)

Interactivity among the visual elements give people an opportunity to do what frustrated internet users love to do — click on everything in an attempt at making something useful happen.

3. Nail Down The Navigation Options

You know what tone you want your business to strike. You’ve got an idea of the visuals you’ll use to present that tone. Your website visitors will think it’s great and fun — but only for a moment. Your website still has to get them to what they’re looking for. Clear navigation is the next big step in directing your lost website visitors toward their goals. A 404 page that’s cute but lacks good navigation design is like that sweet old man who is kind but he gives you the world’s worst directions.

“After making a good first impression with your 404 page, the immediate next step should be getting website visitors off it and to where they want to be. There should always be clear indications on where they should go next.”

For example, Shutterstock’s 404 page offers three distinct options. Visitors can either go back to the previous page, which is helpful if they clicked on the wrong link. They can return to the homepage for more of a hard restart in their navigation, or maybe they came in from a search engine and found a broken link, but they’re not quite ready to give up on the website and want to look around. The final option is to report a problem. If someone has been scouring your website for minutes on end and they have an idea of what they’re looking for, they can report that they might have found an issue. At the very least, it gets your web visitors involved with your company and your development team gets feedback about the accessibility of your website.

(Large preview)

In addition to clear navigation, these other navigation-based elements could help your visitors out even more:

  • Chatbots / live chat: Bots are often received one of two ways. Users either find them incredibly annoying or relatively helpful. Bots that pop up within a second of landing on a page often lead visitors to click out of a site entirely as the bot seems intrusive. However, your website can use bots by simply adding a “Click to chat” option. This invites lost visitors who want your help to engage with the bot rather than the bot making a potentially annoying first move.
  • Search Bars: This element can do wonders for websites with a high volume of pages and information. A search bar could also offer up answers to common questions or redirect to an FAQ.

And one final navigation note — make sure those navigation tactics are just as efficient on mobile as they are on desktop. Treat your 404 page as you would any other. In order for it to succeed, it should be easily navigable to a variety of users, especially in a mobile-first world.

While the look of your 404 page is critical, you ideally never want anyone to find it on your website. Knowing the most common 404 errors on your website could give you insights in how to reduce those issues.

How To Track 404 Events Using Google Analytics What You Need To Start Tracking

The code provided will report 404 events within Google Analytics, so you must have an up-and-running account there to take advantage of this tutorial. You also need access to your site’s 404 template and (this is important) the 404 page must preserve the URL structure of the typed/clicked page. This means that your 404 events can’t just redirect to your 404 page. You must serve the 404 template dynamically with the exact URL that is throwing the error. Most web server services (such as Apache) allow you to do this with a variety of rewrite rules.

(Large preview) Tracking 404 Errors With Google Analytics

With Google Analytics, tracking explicit 404 errors is straightforward and simple. Just ensure that your main Google Analytics tracking script is in place and then add the following code to your 404 Error Page/Template:

<script> // Create Tracker - Send to GA ga('create', 'UA-11111111-11'); ga('send', { hitType: 'event', eventCategory: '404 Response', eventAction: window.location.href, eventLabel: document.referrer }); </script>

You will need to swap out the ID of your specific Google Analytics account. After that, the script works by sending an “event” to Google Analytics. The category is “404 Response," the action uses JavaScript to pass the URL that throws the error, and the label uses JavaScript to pass along the previous URL the user was on. Through all of this data, you can then see what URLs cause 404 events and where people are accessing those URLs.

Tracking 404 Errors With Google Tag Manager

More and more web managers have decided to move to Google Tag Manager. This tool gives them the capability of embedding a whole host of scripts through a single container. It's especially useful if you have a lot of tracking scripts from several providers. To begin tracking 404s through Tag Manager, first begin by creating a “Variable” called “Page Title Variable.” This variable type is a “JavaScript” variable and the Variable Name is “document.title”:

(Large preview)

Essentially, we’re creating a variable that checks for a page’s given title. This is how we will check if we are on a 404 page.

Then create a “Trigger” called “404 Page Title Trigger.” The type is “Page View” and the trigger fires when the “Page Title Variable” contains “404 — Page Not Found” or whatever it is your 404 page title displays as within the browser.

(Large preview)

Lastly, you will need to create a “Tag” called “404 Event Tag.” The tag type is “Universal Analytics” and contains the following components:

(Large preview)

The Variable, Trigger, and Tag all work to pass along the relevant data directly to Google Analytics.

404 Event Reporting

No matter your tracking method (be it through Tag Manager or direct event beacons), your reporting should be the same within Google Analytics. Under “Behavior,” you will see an item called “Events.” Here you will see all reported 404 events. The “Event Action” and “Event Label” dimensions will give you the pertinent data of what URLs are throwing 404 errors and their referring source.

(Large preview)

With this in place, you can now regularly monitor your 404 errors and take the necessary steps to minimize their occurrence. In doing so, you optimize your referral sources and provide the best user experience, keeping conversions and engagement on the right path.

What To Do With Your Google Analytics Results

Now that you know how to monitor those 404 errors, what’s a developer to do? The key takeaway from tracking 404 occurrences is to look for patterns that result in those errors. The data should help you determine user intent, cluing you into what your users want. Ideally, you’ll see trends in what brings people to your 404 page, and you can apply that knowledge to adjust your website accordingly.

If your website visitors are stumbling while searching for a page, take the opportunity to create content that fills in that hole. That way people get results they hadn’t previously seen from your site.

The 404 events could be avoided with a tweak in your website’s design. Make sure the navigation on your pages are clear and direct users to logical ending points. The fix could even be as simple as changing descriptions on a page to paint a clearer picture for users.

Putting It All Together

Tone, images, and navigation — these three elements can transform any 404 page from a ghost town into a pleasant serendipitous stop for your website visitors. And while you don’t want them to stay there forever, you can certainly make sure they stay with you is enjoyable before sending them on their way. By regularly monitoring your 404 errors, you can also alleviate some of the ditches, poorly-marked signage, and potholes that frequently derail users. Being proactive and reactive with 404 errors ultimately improves the journey and the destination for your website visitors.

(yk, ra)
Categories: Around The Web

Colorful Inspiration For Gray Days (November 2018 Wallpapers)

Smashing Magazine - Wed, 10/31/2018 - 4:38am
Colorful Inspiration For Gray Days (November 2018 Wallpapers) Colorful Inspiration For Gray Days (November 2018 Wallpapers) Cosima Mielke 2018-10-31T10:38:11+01:00 2018-11-14T13:45:20+00:00

How about some colorful inspiration for those gray and misty November days? We might have something for you. Just like every month since more than nine years already, artists and designers from across the globe once again tickled their creativity and designed unique wallpapers that are bound to breathe some fresh life into your desktop.

The wallpapers all come in versions with and without a calendar for November 2018 and can be downloaded for free. As a little bonus goodie, we added a selection of favorites from past November editions to this post. Because, well, some things are too good to be forgotten somewhere down in the archives, right? Enjoy!

Further Reading on SmashingMag:

Please note that:

  • All images can be clicked on and lead to the preview of the wallpaper,
  • You can feature your work in our magazine by taking part in our Desktop Wallpaper Calendar series. We are regularly looking for creative designers and artists to be featured on Smashing Magazine. Are you one of them?

Meet Smashing Book 6 — our brand new book focused on real challenges and real front-end solutions in the real world: from design systems and accessible single-page apps to CSS Custom Properties, CSS Grid, Service Workers, performance, AR/VR and responsive art direction. With Marcy Sutton, Yoav Weiss, Lyza D. Gardner, Laura Elizabeth and many others.

Table of Contents → Outer Space

“This November, we are inspired by the nature around us and the universe above us, so we created an out of this world calendar. Now, let us all stop for a second and contemplate on preserving our forests, let us send birds of passage off to warmer places, and let us think to ourselves — if not on Earth, could we find a home somewhere else in outer space?” — Designed by PopArt Studio from Serbia.

Stars

“I don’t know anyone who hasn’t enjoyed a cold night looking at the stars.” — Designed by Ema Rede from Portugal.

Running Through Autumn Mist

“A small tribute to my German Shepherd who adds joy to those grey November days.” — Designed by Franke Margrete from The Netherlands.

Magical Foliage

“Foliage is the most mystical process of nature to ever occur. Leaves bursting and falling in shades of red, orange, yellow and making the landscape look magical.” — Designed by ATop Digital from India.

Sad Kitty

Designed by Ricardo Gimenes from Sweden.

The Light Of Lights

“Diwali is all about celebrating the victory of good over evil and light over darkness. The hearts of the vast majority are as dark as the night of the new moon. The house is lit with lamps, but the heart is full of the darkness of ignorance. Wake up from the slumber of ignorance. Realize the constant and eternal light of the Soul which neither rises nor sets through meditation and make this festive month even brighter and more vibrant.” — Designed by Intranet Software from India.

Her

“I already had an old sketch that I wanted to try to convert to a digital illustration. The colors of the drawing were inspired by nature that at this time of the year has both the warm of fallen leaves as it has the cold greens of the leaves that make it through winter.” — Designed by Ana Matos from Portugal.

Mesmerizing Monsoon

“Monsoon is all about the chill, the tranquillity that whizzes around, a light drizzle that splashes off our faces, the musty aroma of the earth and more than anything - a sense of liberation. The designer here has depicted the soul of monsoon, one that you would want to heartily soak in.” — Designed by Nafis Mohamed from London.

Universal Children’s Day

“Universal Children’s Day, 20 November. It feels like a dream world, it invites you to let your imagination flow, see the details, and find the child inside you.” — Designed by Luis Costa from Portugal.

Stay Little

“It is believed that childhood is the happiest time cause this period of life cannot be matched with any other phases of life. During this month of November, let’s continue celebrating Children’s Day no matter how old you are, by sharing wishes to your children and childhood friends.” — Designed by Taxi Software from India.

Gezelligheid

“This month’s wallpaper is dedicated to the magical place of Barcelona that has filled my soul with renewed purpose and hope. I wanted to recreate the enchanting Parc Güell where I’m celebrating Thanksgiving with the people I’ve met that have given me so much in so little time.” — Designed by Priscilla Li from the United States.

Falling Rainbow

“I have a maple tree in my yard that sometimes turns these colors in the fall - red on the outer leaves, then yellow, and the inner leaves still green.” — Designed by Hannah Joy Patterson from South Carolina, USA.

Origami In The Night Sky

Designed by Rita Gaspar from Portugal.

Oldies But Goodies

Below you’ll find some November goodies from past years. Please note that these wallpapers don’t come with a calendar.

Colorful Autumn

“Autumn can be dreary, especially in November, when rain starts pouring every day. We wanted to summon better days, so that’s how this colourful November calendar was created. Open your umbrella and let’s roll!” — Designed by PopArt Studio from Serbia.

Time To Give Thanks!

Designed by Glynnis Owen from Australia.

No Shave Movember

“The goal of Movember is to ‘change the face of men’s health.’” — Designed by Suman Sil from India.

Welcome Home Dear Winter

“The smell of winter is lingering in the air. The time to be home! Winter reminds us of good food, of the warmth, the touch of a friendly hand, and a talk beside the fire. Keep calm and let us welcome winter.” — Designed by Acodez IT Solutions from India.

Deer Fall, I Love You!

Designed by Maria Porter from the United States.

Mushroom Season!

“It is autumn! It is raining and thus… it is mushroom season! It is the perfect moment to go to the forest and get the best mushrooms to do the best recipe.” — Designed by Verónica Valenzuela from Spain.

Little Mademoiselle P

“Black-and-white drawing of a little girl.” Designed by Jelena Tšekulajeva from Estonia.

Autumn Wreath

“I love the changing seasons — especially the autumn colors and festivals here around this time of year!” — Designed by Rachel Litzinger from the United States.

November Nights On Mountains

“Those chill November nights when you see mountain tops covered with the first snow sparkling in the moonlight.” — Designed by Jovana Djokic from Serbia.

Hello World, Happy November!

“I often read messages at Smashing Magazine from the people in the southern hemisphere ‘it’s spring, not autumn!’ so I’d liked to design a wallpaper for the northern and the southern hemispheres. Here it is, northerners and southerns, hope you like it!” — Designed by Agnes Swart from the Netherlands.

A Gentleman’s November

Designed by Cedric Bloem from Belgium.

Branches

“The design of trees has always fascinated me. Each one has it’s own unique entanglement of branches. With or without leaves they are always intriguing. Take some time to enjoy the trees around you — and the one on this wallpaper if you’d like!” — Designed by Rachel Litzinger from Chiang Rai, Thailand.

Simple Leaves

Designed by Nicky Somers from Belgium.

Captain’s Home

Designed by Elise Vanoorbeek (Doud) from Belgium.

Me And the Key Three

“This wallpaper is based on screenshots from my latest browser game (I’m an indie games designer).” — Designed by Bart Bonte from Belgium.

Red Leaves

Designed by Evacomics from Singapore.

Autumn Choir

Designed by Hatchers from Ukraine / China.

Real Artists Ship

“A tribute to Steve Jobs, from the crew at Busy Building Things.” Designed by Andrew Power from Canada.

Late Autumn

“The late arrival of Autumn.” Designed by Maria Castello Solbes from Spain.

Autumn Impression

Designed by Agnieszka Malarczyk from Poland.

Flying

Designed by Nindze.com from Russia.

Join In Next Month!

Please note that we respect and carefully consider the ideas and motivation behind each and every artist’s work. This is why we give all artists the full freedom to explore their creativity and express emotions and experience throughout their works. This is also why the themes of the wallpapers weren’t anyhow influenced by us, but rather designed from scratch by the artists themselves.

Thank you to all designers for their participation. Join in next month!

Categories: Around The Web

Measuring Performance With Server Timing

Smashing Magazine - Mon, 10/29/2018 - 8:40pm
Measuring Performance With Server Timing Measuring Performance With Server Timing Drew McLellan 2018-10-30T03:40:14+02:00 2018-11-14T13:45:20+00:00

When undertaking any sort of performance optimisation work, one of the very first things we learn is that before you can improve performance you must first measure it. Without being able to measure the speed at which something is working, we can’t tell if the changes being made are improving the performance, having no effect, or even making things worse.

Many of us will be familiar with working on a performance problem at some level. That might be something as simple as trying to figure out why JavaScript on your page isn’t kicking in soon enough, or why images are taking too long to appear on bad hotel wifi. The answer to these sorts of questions is often found in a very familiar place: your browser’s developer tools.

Over the years developer tools have been improved to help us troubleshoot these sorts of performance issues in the front end of our applications. Browsers now even have performance audits built right in. This can help track down front end issues, but these audits can show up another source of slowness that we can’t fix in the browser. That issue is slow server response times.

“Time to First Byte”

There’s very little browser optimisations can do to improve a page that is simply slow to build on the server. That cost is incurred between the browser making the request for the file and receiving the response. Studying your network waterfall chart in developer tools will show this delay up under the category of “Waiting (TTFB)”. This is how long the browser waits between making the request and receiving the response.

Getting workflow just right ain’t an easy task. So are proper estimates. Or alignment among different departments. That’s why we’ve set up “this-is-how-I-work”-sessions — with smart cookies sharing what works well for them. A part of the Smashing Membership, of course.

Explore Smashing Membership ↬

In performance terms this is know as Time to First Byte - the amount of time it takes before the server starts sending something the browser can begin to work with. Encompassed in that wait time is everything the server needs to do to build the page. For a typical site, that might involve routing the request to the correct part of the application, authenticating the request, making multiple calls to backend systems such as databases and so on. It could involve running content through templating systems, making API calls out to third party services, and maybe even things like sending emails or resizing images. Any work that the server does to complete a request is squashed into that TTFB wait that the user experiences in their browser.

Inspecting a document request shows the time the browser spends waiting for the response from the server.

So how do we reduce that time and start delivering the page more quickly to the user? Well, that’s a big question, and the answer depends on your application. That is the work of performance optimisation itself. What we need to do first is measure the performance so that the benefit of any changes can be judged.

The Server Timing Header

The job of Server Timing is not to help you actually time activity on your server. You’ll need to do the timing yourself using whatever toolset your backend platform makes available to you. Rather, the purpose of Server Timing is to specify how those measurements can be communicated to the browser.

The way this is done is very simple, transparent to the user, and has minimal impact on your page weight. The information is sent as a simple set of HTTP response headers.

Server-Timing: db;dur=123, tmpl;dur=56

This example communicates two different timing points named db and tmpl. These aren’t part of the spec - these are names that we’ve picked, in this case to represent some database and template timings respectively.

The dur property is stating the number of milliseconds the operation took to complete. If we look at the request in the Network section of Developer Tools, we can see that the timings have been added to the chart.

A new Server Timing section appears, showing the timings set with the Server-Timing HTTP header.

The Server-Timing header can take multiple metrics separated by commas:

Server-Timing: metric, metric, metric

Each metric can specify three possible properties

  1. A short name for the metric (such as db in our example)
  2. A duration in milliseconds (expressed as dur=123)
  3. A description (expressed as desc="My Description")

Each property is separated with a semicolon as the delimiter. We could add descriptions to our example like so:

Server-Timing: db;dur=123;desc="Database", tmpl;dur=56;desc="Template processing" The names are replaced with descriptions when provided.

The only property that is required is name. Both dur and desc are optional, and can be used optionally where required. For example, if you needed to debug a timing problem that was happening on one server or data center and not another, it might be useful to add that information into the response without an associated timing.

Server-Timing: datacenter;desc="East coast data center", db;dur=123;desc="Database", tmpl;dur=56;desc="Template processing”

This would then show up along with the timings.

The "East coast data center" value is shown, even though it has no timings.

One thing you might notice is that the timing bars don’t show up in a waterfall pattern. This is simply because Server Timing doesn’t attempt to communicate the sequence of timings, just the raw metrics themselves.

Implementing Server Timing

The exact implementation within your own application is going to depend on your specific circumstance, but the principles are the same. The steps are always going to be:

  1. Time some operations
  2. Collect together the timing results
  3. Output the HTTP header

In pseudocode, the generation of response might look like this:

startTimer('db') getInfoFromDatabase() stopTimer('db') startTimer('geo') geolocatePostalAddressWithAPI('10 Downing Street, London, UK') endTimer('geo') outputHeader('Server-Timing', getTimerOutput())

The basics of implementing something along those lines should be straightforward in any language. A very simple PHP implementation could use the microtime() function for timing operations, and might look something along the lines of the following.

class Timers { private $timers = []; public function startTimer($name, $description = null) { $this->timers[$name] = [ 'start' => microtime(true), 'desc' => $description, ]; } public function endTimer($name) { $this->timers[$name]['end'] = microtime(true); } public function getTimers() { $metrics = []; if (count($this->timers)) { foreach($this->timers as $name => $timer) { $timeTaken = ($timer['end'] - $timer['start']) * 1000; $output = sprintf('%s;dur=%f', $name, $timeTaken); if ($timer['desc'] != null) { $output .= sprintf(';desc="%s"', addslashes($timer['desc'])); } $metrics[] = $output; } } return implode($metrics, ', '); } }

A test script would use it as below, here using the usleep() function to artificially create a delay in the running of the script to simulate a process that takes time to complete.

$Timers = new Timers(); $Timers->startTimer('db'); usleep('200000'); $Timers->endTimer('db'); $Timers->startTimer('tpl', 'Templating'); usleep('300000'); $Timers->endTimer('tpl'); $Timers->startTimer('geo', 'Geocoding'); usleep('400000'); $Timers->endTimer('geo'); header('Server-Timing: '.$Timers->getTimers());

Running this code generated a header that looked like this:

Server-Timing: db;dur=201.098919, tpl;dur=301.271915;desc="Templating", geo;dur=404.520988;desc="Geocoding" The Server Timings set in the example show up in the Timings panel with the delays configured in our test script. Existing Implementations

Considering how handy Server Timing is, there are relatively few implementations that I could find. The server-timing NPM package offers a convenient way to use Server Timing from Node projects.

If you use a middleware based PHP framework tuupola/server-timing-middleware provides a handy option too. I’ve been using that in production on Notist for a few months, and I always leave a few basic timings enabled if you’d like to see an example in the wild.

For browser support, the best I’ve seen is in Chrome DevTools, and that’s what I’ve used for the screenshots in this article.

Considerations

Server Timing itself adds very minimal overhead to the HTTP response sent back over the wire. The header is very minimal and is generally safe to be sending without worrying about targeting to only internal users. Even so, it’s worth keeping names and descriptions short so that you’re not adding unnecessary overhead.

More of a concern is the extra work you might be doing on the server to time your page or application. Adding extra timing and logging can itself have an impact on performance, so it’s worth implementing a way to turn this on and off when required.

Using a Server Timing header is a great way to make sure all timing information from both the front-end and the back-end of your application are accessible in one location. Provided your application isn’t too complex, it can be easy to implement and you can be up and running within a very short amount of time.

If you’d like to read more about Server Timing, you might try the following:

(ra)
Categories: Around The Web

The CSS Working Group At TPAC: What’s New In CSS?

Smashing Magazine - Fri, 10/26/2018 - 3:30pm
The CSS Working Group At TPAC: What’s New In CSS? The CSS Working Group At TPAC: What’s New In CSS? Rachel Andrew 2018-10-26T22:30:30+02:00 2018-11-14T13:45:20+00:00

Last week, I attended W3C TPAC as well as the CSS Working Group meeting there. Various changes were made to specifications, and discussions had which I feel are of interest to web designers and developers. In this article, I’ll explain a little bit about what happens at TPAC, and show some examples and demos of the things we discussed at TPAC for CSS in particular.

What Is TPAC?

TPAC is the Technical Plenary / Advisory Committee Meetings Week of the W3C. A chance for all of the various working groups that are part of the W3C to get together under one roof. The event is in a different part of the world each year, this year it was held in Lyon, France. At TPAC, Working Groups such as the CSS Working Group have their own meetings, just as we do at other times of the year. However, because we are all in one building, it means that people from other groups can more easily come as observers, and cross-working group interests can be discussed.

Attendees of TPAC are typically members of one or more of the Working Groups, working on W3C technologies. They will either be representatives of a member organization or Invited Experts. As with any other meetings of W3C Working Groups, the minutes of all of the discussions held at TPAC will be openly available, usually as IRC logs scribed during the meetings.

The CSS Working Group

The CSS Working Group meet face-to-face at TPAC and on at least two other occasions during the year; this is in addition to our weekly phone calls. At all of our meetings, the various issues raised on the specifications are discussed, and decisions made. Some issues are kept for face-to-face discussions due to the benefits of being able to have them with the whole group, or just being able to all get around a whiteboard or see a demo on screen.

When an issue is discussed in any meeting (whether face-to-face or teleconference), the relevant GitHub issue is updated with the minutes of the discussion. This means if you have an issue you want to keep track of, you can star it and see when it is updated. The full IRC minutes are also posted to the www-style mailing list.

Here is a selection of the things we discussed that I think will be of most interest to you.

Web forms are such an important part of the web, but we design them poorly all the time. The brand new Form Design Patterns book is our new practical guide for people who design, prototype and build all sorts of forms for digital services, products and websites. The eBook is free for Smashing Members.

Check the table of contents ↬ CSS Scrollbars

The CSS Scrollbars specification seeks to give a standard way of styling the size and color of scrollbars. If you have Firefox Nightly, you can test it out. To see the examples below, use Firefox Nightly and enable the flags layout.css.scrollbar-width.enabled and layout.css.scrollbar-color.enabled by visiting http://about:config in Firefox Nightly.

The specification gives us two new properties: scrollbar-width and scrollbar-color. The scrollbar-width property can take a value of auto, thin, none, or length (such as 1em). It looks as if the length value may be removed from the specification. As you can imagine, it would be possible for a web developer to make a very unusable scrollbar by playing with the width, so it may be better to allow the browser to decide the exact width that makes sense but instead to either show thin or thick scrollbars. Firefox has not implemented the length option.

If you use auto as the value, then the browser will use the default scrollbars: thin will give you a thin scrollbar, and none will show no visible scrollbar (but the element will still be scrollable).

In this example I have set scrollbar-width: thin.(Large preview)

In a browser with support for CSS Scrollbars, you can see this in action in the demo:

See the Pen CSS Scrollbars: scrollbar-width by Rachel Andrew (@rachelandrew) on CodePen.

The scrollbar-color property deals with — as you would expect — scrollbar colors. A scrollbar has two parts which you may wish to color independently:

  • thumb
    The slider that moves up and down as you scroll.
  • track
    The scrollbar background.

The values for the scrollbar-color property are auto, dark, light and <color> <color>.

Using auto as a keyword value will give you the default scrollbar colors for that browser, dark will provide a dark scrollbar, either in the dark mode of that platform or a custom dark mode, light the light mode of the platform or a custom light mode.

To set your own colors, you add two colors as the value that are separated by a space. The first color will be used for the thumb and the second one for the track. You should take care that there is enough contrast between the colors, as otherwise the scrollbar may be difficult to use for some people.

In this example, I have set custom colors for the scrollbar elements. (Large preview)

In a browser with support for CSS Scrollbars, you can see this in the demo:

See the Pen CSS Scrollbars: scrollbar-color by Rachel Andrew (@rachelandrew) on CodePen.

Aspect Ratio Units

We’ve been using the padding hack in CSS to achieve aspect ratio boxes for some time, however, with the advent of Grid Layout and better ways of sizing content, having a real way to do aspect ratios in CSS has become a more pressing need.

There are two issues raised on GitHub which relate to this requirement:

There is now a draft spec in Level 4 of CSS Sizing, and the decision of the meeting was that this needed further discussion on GitHub before any decisions can be made. So, if you are interested in this, or have additional use cases, the CSS Working Group would be interested in your comments on those issues.

The :where() Functional Pseudo-Class

Last year, the CSSWG resolved to add a pseudo-class which acted like :matches() but with zero specificity, thus making it easy to override without needing to artificially inflate the specificity of later elements to override something in a default stylesheet.

The :matches() pseudo-class might be new to you as it is a Level 4 Selector, however, it allows you to specify a group of selectors to apply some CSS to. For example, you could write:

.foo a:hover, p a:hover { color: green; }

Or with :matches()

:matches(.foo, p) a:hover { color: green; }

If you have ever had a big stack of selectors just in order to set the same couple of rules, you will see how useful this will be. The following CodePen uses the prefixed names of webkit-any and -moz-any to demonstrate the matches() functionality. You can also read more about matches() on MDN.

See the Pen :matches() and prefixed versions by Rachel Andrew (@rachelandrew) on CodePen.

Where we often do this kind of stacking of selectors, and thus where :matches() will be most useful is in some kind of initial, default stylesheet. However, we then need to be careful when overwriting those defaults that any overwriting is done in a way that will ensure it is more specific than the defaults. It is for this reason that a zero specificity version was proposed.

The issue that was discussed in the meeting was in regard to naming this pseudo-class, you can see the final resolution here, and if you wonder why various names were ruled out take a look at the full thread. Naming things in CSS is very hard — because we are all going to have to live with it forever! After a lot of debate, the group voted and decided to call this selector :where().

Since the meeting, and while I was writing up this post, a suggestion has been raised to rename matches() to is(). Take a look at the issue and comment if you have any strong feelings either way!

Logical Shorthands For Margins And Padding

On the subject of naming things, I’ve written about Logical Properties and Values here on Smashing Magazine in the past, take a look at “Understanding Logical Properties and Values”. These properties and values provide flow relative mappings. This means that if you are using Writing Modes other than a horizontal top to bottom writing mode, such as English, things like margins and padding, widths and height follow the text direction and are not linked to the physical screen dimensions.

For example, for physical margins we have:

  • margin-top
  • margin-right
  • margin-bottom
  • margin-left

The logical mappings for these (assuming horizontal-tb) are:

  • margin-block-start
  • margin-inline-end
  • margin-block-end
  • margin-inline-start

We can have two value shorthands. For example, to set both margin-block-start and margin-block-end as a shorthand, we can use margin-block: 20px 1em. The first value representing the start edge in the block dimension, the second value the end edge in the block dimension.

We hit a problem, however, when we come to the four-value shorthand margin. That property name is used for physical margins — how do we denote the logical four-value version? Various things have been suggested, including a switch at the top of the file:

@mode "logical";

Or, to use a block that looks a little like a media query:

@mode (flow-mode: relative) { }

Then various suggestions for keyword modifiers, using some punctuation character, or creating a brand new property name:

margin: relative 1em 2em 3em 4em; margin: 1em 2em 3em 4em !relative; margin-relative: 1em 2em 3em 4em; ~margin: 1em 2em 3em 4em;

You can read the issue to see the various things that are being considered. Issues discussed were that while the logical version may well end up being generally the default, sometimes you will want things to relate to the screen geometry; we need to be able to have both options in one stylesheet. Having a @mode setting at the top of the CSS could be confusing; it would fail if someone were to copy and paste a chunk of the stylesheet.

My preference is to have some sort of keyword value. That way, if you look at the rule, you can see exactly which mode is being used, even if it does seem slightly inelegant. It is the sort of thing that a preprocessor could deal with for you; if you did indeed want all of your properties and values to use the logical versions.

We didn’t manage to resolve on the issue, so if you do have thoughts on which of these might be best, or can see problems with them that we haven’t described, please comment on the issue on GitHub.

Web Platform Tests Discussion

At the CSS Working Group meeting and then during the unconference style Technical Plenary Day, I was involved in discussing how to get more people involved in writing tests for CSS specifications. The Web Platform Tests project aims to provide tests for all of the web platform. These tests then help browser vendors check whether their browser is correct as to the spec. In the CSS Working Group, the aim is that any normative change to a specification which has reached Candidate Recommendation (CR) status, should be accompanied by a test. This makes sense as once a spec is in CR, we are asking browsers to implement that spec and provide feedback. They need to know if anything in the spec changes so they can update their code.

The problem is that we have very few people writing specs, so for spec writers to have to write all the tests will slow the progress of CSS down. We would love to see other people writing tests, as it is a way to contribute to the web platform and to gain deep knowledge of how specifications work. So we met to think about how we could encourage people to participate in the effort. I’ve written on this subject in the past; if the idea of writing tests for the platform interests you, take a look at my 24 Ways article on “Testing the Web Platform”.

On With The Work!

TPAC has added to my personal to-do list considerably. However, I’ve been able to pick up tips about specification editing, test writing, and to come up with a plan to get the Multi-Column Layout specification — of which I’m the co-editor — back to CR status. As someone who is not a fan of meetings, I’ve come to see how valuable these face-to-face meetings are for the web platform, giving those of us contributing to it a chance to share the knowledge we individually are developing. I feel it is important though to then take that knowledge and share it outside of the group in order to help more people get involved with developing as well as using the platform.

If you are interested in how the CSS Working Group functions, and how new CSS is invented and ends up in browsers, check out my 2017 CSSConf.eu presentation “Where Does CSS Come From?” and the information from fantasai in her posts “An Inside View of the CSS Working Group at W3C”.

(il)
Categories: Around The Web

Headless WordPress: The Ups And Downs Of Creating A Decoupled WordPress

Smashing Magazine - Fri, 10/26/2018 - 6:45am
Headless WordPress: The Ups And Downs Of Creating A Decoupled WordPress Headless WordPress: The Ups And Downs Of Creating A Decoupled WordPress Denis Žoljom 2018-10-26T13:45:46+02:00 2018-11-14T13:45:20+00:00

WordPress came a long way from its start as a simple blog writing tool. A long 15 years later it became the number one CMS choice for developers and non-developers alike. WordPress now powers roughly 30% of the top 10 million sites on the web.

Ever since REST API was bundled in the WordPress core, developers can experiment and use it in a decoupled way, i.e. writing the front-end part by using JavaScript frameworks or libraries. At Infinum, we were (and still are) using WordPress in a ‘classic’ way: PHP for the frontend as well as the backend. After a while, we wanted to give the decoupled approach a go. In this article, I’ll share an overview of what it was that we wanted to achieve and what we encountered while trying to implement our goals.

There are several types of projects that can benefit from this approach. For example, simple presentational sites or sites that use WordPress as a backend are the main candidates for the decoupled approach.

In recent years, the industry thankfully started paying more attention to performance. However, being an easy-to-use inclusive and versatile piece of software, WordPress comes with a plethora of options that are not necessarily utilized in each and every project. As a result, website performance can suffer.

Recommended reading: How To Use Heatmaps To Track Clicks On Your WordPress Website

If long website response times keep you up at night, this is a how-to for you. I will cover the basics of creating a decoupled WordPress and some lessons learned, including:

  1. The meaning of a “decoupled WordPress”
  2. Working with the default WordPress REST API
  3. Improving performance with the decoupled JSON approach
  4. Security concerns

Web forms are such an important part of the web, but we design them poorly all the time. The brand new Form Design Patterns book is our new practical guide for people who design, prototype and build all sorts of forms for digital services, products and websites. The eBook is free for Smashing Members.

Check the table of contents ↬ So, What Exactly Is A Decoupled WordPress?

When it comes down to how WordPress is programmed, one thing is certain: it doesn’t follow the Model-View-Controller (MVC) design pattern that many developers are familiar with. Because of its history and for being sort of a fork of an old blogging platform called “b2” (more details here), it’s largely written in a procedural way (using function-based code). WordPress core developers used a system of hooks which allowed other developers to modify or extend certain functionalities.

It’s an all-in-one system that is equipped with a working admin interface; it manages database connection, and has a bunch of useful APIs exposed that handle user authentication, routing, and more.

But thanks to the REST API, you can separate the WordPress backend as a sort of model and controller bundled together that handle data manipulation and database interaction, and use REST API Controller to interact with a separate view layer using various API endpoints. In addition to MVC separation, we can (for security reasons or speed improvements) place the JS App on a separate server like in the schema below:

Decoupled WordPress diagram. (Large preview) Advantages Of Using The Decoupled Approach

One thing why you may want to use this approach for is to ensure a separation of concerns. The frontend and the backend are interacting via endpoints; each can be on its separate server which can be optimized specifically for each respective task, i.e. separately running a PHP app and running a Node.js app.

By separating your frontend from the backend, it’s easier to redesign it in the future, without changing the CMS. Also, front-end developers only need to care about what to do with the data the backend provides them. This lets them get creative and use modern libraries like ReactJS, Vue or Angular to deliver highly dynamic web apps. For example, it’s easier to build a progressive web app when using the aforementioned libraries.

Another advantage is reflected in the website security. Exploiting the website through the backend becomes more difficult since it’s largely hidden from the public.

Recommended reading: WordPress Security As A Process

Shortcomings Of Using The Decoupled Approach

First, having a decoupled WordPress means maintaining two separate instances:

  1. WordPress for the backend;
  2. A separate front-end app, including timely security updates.

Second, some of the front-end libraries do have a steeper learning curve. It will either take a lot of time to learn a new language (if you are only accustomed to HTML and CSS for templating), or will require bringing additional JavaScript experts to the project.

Third, by separating the frontend, you are losing the power of the WYSIWYG editor, and the ‘Live Preview’ button in WordPress doesn’t work either.

Working With WordPress REST API

Before we delve deeper in the code, a couple more things about WordPress REST API. The full power of the REST API in WordPress came with version 4.7 on December 6th, 2016.

What WordPress REST API allows you to do is to interact with your WordPress installation remotely by sending and receiving JSON objects.

Setting Up A Project

Since it comes bundled with latest WordPress installation, we will be working on the Twenty Seventeen theme. I’m working on Varying Vagrant Vagrants, and have set up a test site with an URL http://dev.wordpress.test/. This URL will be used throughout the article. We’ll also import posts from the wordpress.org Theme Review Teams repository so that we have some test data to work with. But first, we will get familiar working with default endpoints, and then we’ll create our own custom endpoint.

Access The Default REST Endpoint

As already mentioned, WordPress comes with several built-in endpoints that you can examine by going to the /wp-json/ route:

http://dev.wordpress.test/wp-json/

Either by putting this URL directly in your browser, or adding it in the postman app, you’ll get out a JSON response from WordPress REST API that looks something like this:

{ "name": "Test dev site", "description": "Just another WordPress site", "url": "http://dev.wordpress.test", "home": "http://dev.wordpress.test", "gmt_offset": "0", "timezone_string": "", "namespaces": [ "oembed/1.0", "wp/v2" ], "authentication": [], "routes": { "/": { "namespace": "", "methods": [ "GET" ], "endpoints": [ { "methods": [ "GET" ], "args": { "context": { "required": false, "default": "view" } } } ], "_links": { "self": "http://dev.wordpress.test/wp-json/" } }, "/oembed/1.0": { "namespace": "oembed/1.0", "methods": [ "GET" ], "endpoints": [ { "methods": [ "GET" ], "args": { "namespace": { "required": false, "default": "oembed/1.0" }, "context": { "required": false, "default": "view" } } } ], "_links": { "self": "http://dev.wordpress.test/wp-json/oembed/1.0" } }, ... "wp/v2": { ...

So in order to get all of the posts in our site by using REST, we would need to go to http://dev.wordpress.test/wp-json/wp/v2/posts. Notice that the wp/v2/ marks the reserved core endpoints like posts, pages, media, taxonomies, categories, and so on.

So, how do we add a custom endpoint?

Create A Custom REST Endpoint

Let’s say we want to add a new endpoint or additional field to the existing endpoint. There are several ways we can do that. First, one can be done automatically when creating a custom post type. For instance, we want to create a documentation endpoint. Let’s create a small test plugin. Create a test-documentation folder in the wp-content/plugins folder, and add documentation.php file that looks like this:

<?php /** * Test plugin * * @since 1.0.0 * @package test_plugin * * @wordpress-plugin * Plugin Name: Test Documentation Plugin * Plugin URI: * Description: The test plugin that adds rest functionality * Version: 1.0.0 * Author: Infinum * Author URI: https://infinum.co/ * License: GPL-2.0+ * License URI: http://www.gnu.org/licenses/gpl-2.0.txt * Text Domain: test-plugin */ namespace Test_Plugin; // If this file is called directly, abort. if ( ! defined( 'WPINC' ) ) { die; } /** * Class that holds all the necessary functionality for the * documentation custom post type * * @since 1.0.0 */ class Documentation { /** * The custom post type slug * * @var string * * @since 1.0.0 */ const PLUGIN_NAME = 'documentation-plugin'; /** * The custom post type slug * * @var string * * @since 1.0.0 */ const POST_TYPE_SLUG = 'documentation'; /** * The custom taxonomy type slug * * @var string * * @since 1.0.0 */ const TAXONOMY_SLUG = 'documentation-category'; /** * Register custom post type * * @since 1.0.0 */ public function register_post_type() { $args = array( 'label' => esc_html( 'Documentation', 'test-plugin' ), 'public' => true, 'menu_position' => 47, 'menu_icon' => 'dashicons-book', 'supports' => array( 'title', 'editor', 'revisions', 'thumbnail' ), 'has_archive' => false, 'show_in_rest' => true, 'publicly_queryable' => false, ); register_post_type( self::POST_TYPE_SLUG, $args ); } /** * Register custom tag taxonomy * * @since 1.0.0 */ public function register_taxonomy() { $args = array( 'hierarchical' => false, 'label' => esc_html( 'Documentation tags', 'test-plugin' ), 'show_ui' => true, 'show_admin_column' => true, 'update_count_callback' => '_update_post_term_count', 'show_in_rest' => true, 'query_var' => true, ); register_taxonomy( self::TAXONOMY_SLUG, [ self::POST_TYPE_SLUG ], $args ); } } $documentation = new Documentation(); add_action( 'init', [ $documentation, 'register_post_type' ] ); add_action( 'init', [ $documentation, 'register_taxonomy' ] );

By registering the new post type and taxonomy, and setting the show_in_rest argument to true, WordPress automatically created a REST route in the /wp/v2/namespace. You now have http://dev.wordpress.test/wp-json/wp/v2/documentation and http://dev.wordpress.test/wp-json/wp/v2/documentation-category endpoints available. If we add a post in our newly created documentation custom post going to http://dev.wordpress.test/?post_type=documentation, it will give us a response that looks like this:

[ { "id": 4, "date": "2018-06-11T19:48:51", "date_gmt": "2018-06-11T19:48:51", "guid": { "rendered": "http://dev.wordpress.test/?post_type=documentation&p=4" }, "modified": "2018-06-11T19:48:51", "modified_gmt": "2018-06-11T19:48:51", "slug": "test-documentation", "status": "publish", "type": "documentation", "link": "http://dev.wordpress.test/documentation/test-documentation/", "title": { "rendered": "Test documentation" }, "content": { "rendered": "

This is some documentation content

\n", "protected": false }, "featured_media": 0, "template": "", "documentation-category": [ 2 ], "_links": { "self": [ { "href": "http://dev.wordpress.test/wp-json/wp/v2/documentation/4" } ], "collection": [ { "href": "http://dev.wordpress.test/wp-json/wp/v2/documentation" } ], "about": [ { "href": "http://dev.wordpress.test/wp-json/wp/v2/types/documentation" } ], "version-history": [ { "href": "http://dev.wordpress.test/wp-json/wp/v2/documentation/4/revisions" } ], "wp:attachment": [ { "href": "http://dev.wordpress.test/wp-json/wp/v2/media?parent=4" } ], "wp:term": [ { "taxonomy": "documentation-category", "embeddable": true, "href": "http://dev.wordpress.test/wp-json/wp/v2/documentation-category?post=4" } ], "curies": [ { "name": "wp", "href": "https://api.w.org/{rel}", "templated": true } ] } } ]

This is a great starting point for our single-page application. Another way we can add a custom endpoint is by hooking to the rest_api_init hook and creating an endpoint ourselves. Let’s add a custom-documentation route that is a bit different than the one we registered. Still working in the same plugin, we can add:

/** * Create a custom endpoint * * @since 1.0.0 */ public function create_custom_documentation_endpoint() { register_rest_route( self::PLUGIN_NAME . '/v1', '/custom-documentation', array( 'methods' => 'GET', 'callback' => [ $this, 'get_custom_documentation' ], ) ); } /** * Create a callback for the custom documentation endpoint * * @return string JSON that indicates success/failure of the update, * or JSON that indicates an error occurred. * @since 1.0.0 */ public function get_custom_documentation() { /* Some permission checks can be added here. */ // Return only documentation name and tag name. $doc_args = array( 'post_type' => self::POST_TYPE_SLUG, 'post_status' => 'publish', 'perm' => 'readable' ); $query = new \WP_Query( $doc_args ); $response = []; $counter = 0; // The Loop if ( $query->have_posts() ) { while ( $query->have_posts() ) { $query->the_post(); $post_id = get_the_ID(); $post_tags = get_the_terms( $post_id, self::TAXONOMY_SLUG ); $response[ $counter ]['title'] = get_the_title(); foreach ( $post_tags as $tags_key => $tags_value ) { $response[ $counter ]['tags'][] = $tags_value->name; } $counter++; } } else { $response = esc_html__( 'There are no posts.', 'documentation-plugin' ); } /* Restore original Post Data */ wp_reset_postdata(); return rest_ensure_response( $response ); }

And hook the create_custom_documentation_endpoint() method to the rest_api_init hook, like so:

add_action( 'rest_api_init', [ $documentation, 'create_custom_documentation_endpoint' ] );

This will add a custom route in the http://dev.wordpress.test/wp-json/documentation-plugin/v1/custom-documentation with the callback returning the response for that route.

[{ "title": "Another test documentation", "tags": ["Another tag"] }, { "title": "Test documentation", "tags": ["REST API", "test tag"] }]

There are a lot of other things you can do with REST API (you can find more details in the REST API handbook).

Work Around Long Response Times When Using The Default REST API

For anyone who has tried to build a decoupled WordPress site, this is not a new thing — REST API is slow.

My team and I first encountered the strange WordPress-lagging REST API on a client site (not decoupled), where we used the custom endpoints to get the list of locations on a Google map, alongside other meta information created using the Advanced Custom Fields Pro plugin. It turned out that the time the first byte (TTFB) — which is used as an indication of the responsiveness of a web server or other network resource — took more than 3 seconds.

After a bit of investigating, we realized the default REST API calls were actually really slow, especially when we “burdened” the site with additional plugins. So, we did a small test. We installed a couple of popular plugins and encountered some interesting results. The postman app gave the load time of 1.97s for 41.9KB of response size. Chrome’s load time was 1.25s (TTFB was 1.25s, content was downloaded in 3.96ms). Just to retrieve a simple list of posts. No taxonomy, no user data, no additional meta fields.

Why did this happen?

It turns out that accessing REST API on the default WordPress will load the entire WordPress core to serve the endpoints, even though it’s not used. Also, the more plugins you add, the worse things get. The default REST controller WP_REST_Controller is a really big class that does a lot more than necessary when building a simple web page. It handles routes registering, permission checks, creating and deleting items, and so on.

There are two common workarounds for this issue:

  1. Intercept the loading of the plugins, and prevent loading them all when you need to serve a simple REST response;
  2. Load only the bare minimum of WordPress and store the data in a transient, from which we then fetch the data using a custom page.
Improving Performance With The Decoupled JSON Approach

When you are working with simple presentation sites, you don’t need all the functionality REST API offers you. Of course, this is where good planning is crucial. You really don’t want to build your site without REST API, and then say in a years time that you’d like to connect to your site, or maybe create a mobile app that needs to use REST API functionality. Do you?

For that reason, we utilized two WordPress features that can help you out when serving simple JSON data out:

  • Transients API for caching,
  • Loading the minimum necessary WordPress using SHORTINIT constant.
Creating A Simple Decoupled Pages Endpoint

Let’s create a small plugin that will demonstrate the effect that we’re talking about. First, add a wp-config-simple.php file in your json-transient plugin that looks like this:

<?php /** * Create simple wp configuration for the routes * * @since 1.0.0 * @package json-transient */ define( 'SHORTINIT', true ); $parse_uri = explode( 'wp-content', $_SERVER['SCRIPT_FILENAME'] ); require_once filter_var( $parse_uri[0] . 'wp-load.php', FILTER_SANITIZE_STRING );

The define( 'SHORTINIT', true ); will prevent the majority of WordPress core files to be loaded, as can be seen in the wp-settings.php file.

We still may need some of the WordPress functionality, so we can require the file (like wp-load.php) manually. Since wp-load.php sits in the root of our WordPress installation, we will fetch it by getting the path of our file using $_SERVER['SCRIPT_FILENAME'], and then exploding that string by wp-content string. This will return an array with two values:

  1. The root of our installation;
  2. The rest of the file path (which is of no interest to us).

Keep in mind that we’re using the default installation of WordPress, and not a modified one, like for example in the Bedrock boilerplate, which splits the WordPress in a different file organization.

Lastly, we require the wp-load.php file, with a little bit of sanitization, for security.

In our init.php file, we’ll add the following:

<?php /** * Test plugin * * @since 1.0.0 * @package json-transient * * @wordpress-plugin * Plugin Name: Json Transient * Plugin URI: * Description: Proof of concept for caching api like calls * Version: 1.0.0 * Author: Infinum * Author URI: https://infinum.co/ * License: GPL-2.0+ * License URI: http://www.gnu.org/licenses/gpl-2.0.txt * Text Domain: json-transient */ namespace Json_Transient; // If this file is called directly, abort. if ( ! defined( 'WPINC' ) ) { die; } class Init { /** * Get the array of allowed types to do operations on. * * @return array * * @since 1.0.0 */ public function get_allowed_post_types() { return array( 'post', 'page' ); } /** * Check if post type is allowed to be save in transient. * * @param string $post_type Get post type. * @return boolean * * @since 1.0.0 */ public function is_post_type_allowed_to_save( $post_type = null ) { if( ! $post_type ) { return false; } $allowed_types = $this->get_allowed_post_types(); if ( in_array( $post_type, $allowed_types, true ) ) { return true; } return false; } /** * Get Page cache name for transient by post slug and type. * * @param string $post_slug Page Slug to save. * @param string $post_type Page Type to save. * @return string * * @since 1.0.0 */ public function get_page_cache_name_by_slug( $post_slug = null, $post_type = null ) { if( ! $post_slug || ! $post_type ) { return false; } $post_slug = str_replace( '__trashed', '', $post_slug ); return 'jt_data_' . $post_type . '_' . $post_slug; } /** * Get full post data by post slug and type. * * @param string $post_slug Page Slug to do Query by. * @param string $post_type Page Type to do Query by. * @return array * * @since 1.0.0 */ public function get_page_data_by_slug( $post_slug = null, $post_type = null ) { if( ! $post_slug || ! $post_type ) { return false; } $page_output = ''; $args = array( 'name' => $post_slug, 'post_type' => $post_type, 'posts_per_page' => 1, 'no_found_rows' => true ); $the_query = new \WP_Query( $args ); if ( $the_query->have_posts() ) { while ( $the_query->have_posts() ) { $the_query->the_post(); $page_output = $the_query->post; } wp_reset_postdata(); } return $page_output; } /** * Return Page in JSON format * * @param string $post_slug Page Slug. * @param string $post_type Page Type. * @return json * * @since 1.0.0 */ public function get_json_page( $post_slug = null, $post_type = null ) { if( ! $post_slug || ! $post_type ) { return false; } return wp_json_encode( $this->get_page_data_by_slug( $post_slug, $post_type ) ); } /** * Update Page to transient for caching on action hooks save_post. * * @param int $post_id Saved Post ID provided by action hook. * * @since 1.0.0 */ public function update_page_transient( $post_id ) { $post_status = get_post_status( $post_id ); $post = get_post( $post_id ); $post_slug = $post->post_name; $post_type = $post->post_type; $cache_name = $this->get_page_cache_name_by_slug( $post_slug, $post_type ); if( ! $cache_name ) { return false; } if( $post_status === 'auto-draft' || $post_status === 'inherit' ) { return false; } else if( $post_status === 'trash' ) { delete_transient( $cache_name ); } else { if( $this->is_post_type_allowed_to_save( $post_type ) ) { $cache = $this->get_json_page( $post_slug, $post_type ); set_transient( $cache_name, $cache, 0 ); } } } } $init = new Init(); add_action( 'save_post', [ $init, 'update_page_transient' ] );

The helper methods in the above code will enable us to do some caching:

  • get_allowed_post_types()
    This method lets post types know that we want to enable showing in our custom ‘endpoint’. You can extend this, and the plugin we’ve actually made this method filterable so that you can just use a filter to add additional items.
  • is_post_type_allowed_to_save()
    This method simply checks to see if the post type we’re trying to fetch the data from is in the allowed array specified by the previous method.
  • get_page_cache_name_by_slug()
    This method will return the name of the transient that the data will be fetched from.
  • get_page_data_by_slug()
    This method is the method that will perform the WP_Query on the post via its slug and post type and return the contents of the post array that we’ll convert with the JSON using the get_json_page() method.
  • update_page_transient()
    This will be run on the save_post hook and will overwrite the transient in the database with the JSON data of our post. This last method is known as the “key caching method”.

Let’s explain transients in more depth.

Transients API

Transients API is used to store data in the options table of your WordPress database for a specific period of time. It’s a persisted object cache, meaning that you are storing some object, for example, results of big and slow queries or full pages that can be persisted across page loads. It is similar to regular WordPress Object Cache, but unlike WP_Cache, transients will persist data across page loads, where WP_Cache (storing the data in memory) will only hold the data for the duration of a request.

It’s a key-value store, meaning that we can easily and quickly fetch the desired data, similar to what in-memory caching systems like Memcached or Redis do. The difference is that you’d usually need to install those separately on the server (which can be an issue on shared servers), whereas transients are built in with WordPress.

As noted on its Codex page — transients are inherently sped up by caching plugins. Since they can store transients in memory instead of a database. The general rule is that you shouldn’t assume that transient is always present in the database — which is why it’s a good practice to check for its existence before fetching it

$transient_name = get_transient( 'transient_name' ); if ( $transient_name === false ) { set_transient( 'transient_name', $transient_data, $transient_expiry ); }

You can use it without expiration (like we are doing), and that’s why we implemented a sort of ‘cache-busting’ on post save. In addition to all the great functionality they provide, they can hold up to 4GB of data in it, but we don’t recommend storing anything that big in a single database field.

Recommended reading: Be Watchful: PHP And WordPress Functions That Can Make Your Site Insecure

Final Endpoint: Testing And Verification

The last piece of the puzzle that we need is an ‘endpoint’. I’m using the term endpoint here, even though it’s not an endpoint since we are directly calling a specific file to fetch our results. So we can create a test.php file that looks like this:

<?php /** * Generate rest doute * * Route location: /wp-content/plugins/json-transient/test.php?slug=sample-page&type=page * * @since 1.0.0 * @package json-transient */ // Load simple version of WordPress, this file can be located anywhere. require_once 'wp-config-simple.php'; // Load function to be able to call some functions. require_once 'init.php'; $init = new Json_Transient\Init(); // Check input and protect it. if ( ( isset( $_GET['slug'] ) || ! empty( $_GET['slug'] ) ) && ( isset( $_GET['type'] ) || ! empty( $_GET['type'] ) ) ) { $post_slug = htmlentities( trim ( $_GET['slug'] ), ENT_QUOTES ); $post_type = htmlentities( trim ( $_GET['type'] ), ENT_QUOTES ); } else { wp_send_json( 'Error, page slug or type is missing!' ); } // Get transient by name. $cache = get_transient( $init->get_page_cache_name_by_slug( $post_slug, $post_type ) ); // Return error on false. if ( $cache === false ) { wp_send_json( 'Error, the page does not exist or it is not cached correctly. Please try rebuilding cache and try again!' ); } // Decode json for output. wp_send_json( json_decode( $cache ) );

If we go to http://dev.wordpress.test/wp-content/plugins/json-transient/test.php, we’ll see this message:

Error, page slug or type is missing!

So, we’ll need to specify the post type and post slug. When we now go to http://dev.wordpress.test/wp-content/plugins/json-transient/test.php?slug=hello-world&type=post we’ll see:

Error, the page does not exist or it is not cached correctly. Please try rebuilding cache and try again!

Oh, wait! We need to re-save our pages and posts first. So when you’re starting out, this can be easy. But if you already have 100+ pages or posts, this can be a challenging task. This is why we implemented a way to clear the transients in the Decoupled JSON Content plugin, and rebuild them in a batch.

But go ahead and re-save the Hello World post and then open the link again. What you should now have is something that looks like this:

{ "ID": 1, "post_author": "1", "post_date": "2018-06-26 18:28:57", "post_date_gmt": "2018-06-26 18:28:57", "post_content": "Welcome to WordPress. This is your first post. Edit or delete it, then start writing!", "post_title": "Hello world!", "post_excerpt": "", "post_status": "publish", "comment_status": "open", "ping_status": "open", "post_password": "", "post_name": "hello-world", "to_ping": "", "pinged": "", "post_modified": "2018-06-30 08:34:52", "post_modified_gmt": "2018-06-30 08:34:52", "post_content_filtered": "", "post_parent": 0, "guid": "http:\/\/dev.wordpress.test\/?p=1", "menu_order": 0, "post_type": "post", "post_mime_type": "", "comment_count": "1", "filter": "raw" }

And that’s it. The plugin we made has some more extra functionality that you can use, but in a nutshell, this is how you can fetch the JSON data from your WordPress that is way faster than using REST API.

Before And After: Improved Response Time

We conducted testing in Chrome, where we could see the total response time and the TTFB separately. We tested response times ten times in a row: first without plugins and then with the plugins added. Also, we tested the response for a list of posts and for a single post.

The results of the test are illustrated in the tables below:

Comparison graph depicting response times of using WordPress REST API vs using the decoupled approach without added plugins. The decoupled approach is 2 to 3 times faster. (Large preview) Comparison graph depicting response times of using WordPress REST API vs using the decoupled approach with added plugins. The decoupled approach is up to 8 times faster. (Large preview)

As you can see, the difference is drastic.

Security Concerns

There are some caveats that you’ll need to take a good look at. First of all, we are manually loading WordPress core files, which in the WordPress world is a big no-no. Why? Well, besides the fact that manually fetching core files can be tricky (especially if you’re using nonstandard installations such as Bedrock), it could pose some security concerns.

If you decide to use the method described in this article, be sure you know how to fortify your server security.

First, add HTML headers like in the test.php file:

header( 'Access-Control-Allow-Origin: your-front-end-app.url' ); header( 'Content-Type: application/json' );

The first header is a way to bypass CORS security measure so that only your front-end app can fetch the contents when going to the specified file.

Second, disable directory traversal of your app. You can do this by modifying nginx settings, or add Options -Indexes to your .htaccess file if you’re on an Apache server.

Adding a token check to the response is also a good measure that can prevent unwanted access. We are actually working on a way to modify our Decoupled JSON plugin so that we can include these security measures by default.

A check for an Authorization header sent by the frontend app could look like this:

if ( ! isset( $_SERVER['HTTP_AUTHORIZATION'] ) ) { return; } $auth_header = $_SERVER['HTTP_AUTHORIZATION'];

Then you can check if the specific token (a secret that is only shared by the front- and back-end apps) is provided and correct.

Conclusion

REST API is great because it can be used to create fully-fledged apps — creating, retrieving, updating and deleting the data. The downside of using it is its speed.

Obviously, creating an app is different than creating a classic website. You probably won’t need all the plugins we installed. But if you just need the data for presentational purposes, caching data and serving it in a custom file seems like the perfect solution at the moment, when working with decoupled sites.

You may be thinking that creating a custom plugin to speed up the website response time is an overkill, but we live in a world in which every second counts. Everyone knows that if a website is slow, users will abandon it. There are many studies that demonstrate the connection between website performance and conversion rates. But if you still need convincing, Google penalizes slow websites.

The method explained in this article solves the speed issue that the WordPress REST API encounters and will give you an extra boost when working on a decoupled WordPress project. As we are on our never-ending quest to squeeze out that last millisecond out of every request and response, we plan to optimize the plugin even more. In the meantime, please share your ideas on speeding up decoupled WordPress!

(md, ra, yk, il)
Categories: Around The Web

Video Playback On The Web: Video Delivery Best Practices (Part 2)

Smashing Magazine - Thu, 10/25/2018 - 7:40am
Video Playback On The Web: Video Delivery Best Practices (Part 2) Video Playback On The Web: Video Delivery Best Practices (Part 2) Doug Sillars 2018-10-25T14:40:24+02:00 2018-11-14T13:45:20+00:00

In my previous post, I examined video trends on the web today, using data from the HTTP Archive. I found that many websites serve the same video content on mobile and desktop, and that many video streams are being delivered at bitrates that are too high to playback on 3G speed connections. We also discovered that may websites automatically download video to mobile devices — damaging customer’s data plans, battery life, for videos that might not ever be played.

TL;DR: In this post, we look at techniques to optimize the speed and delivery of video to your customers, and provide a list of 9 best practices to help you deliver your video assets.

Video Playback Metrics

There are 3 principal video playback metrics in use today:

  1. Video Startup Time
  2. Video Stalling
  3. Video Quality

Since video files are large, optimizing the video to be as small as possible will lead to faster video delivery, speeding up video start, lowering the number of stalls, and minimizing the effect of the quality of the video delivered. Of course, we need to balance startup speed and stalling with the third metric of quality (and higher quality videos generally use more data).

Video Startup

When a user presses play on a video, they expect to be able to watch the video quickly. According to Conviva (a leader in video metric analysis), in Q1 of 2018, 14% of videos never started playing (that’s 2.4 Billion video plays) after the user pressed play.

Video Start Breakdown (Large preview)

2.3% of videos (400M video requests) failed to play after the user pressed the play button. 11.54% (2B plays) were abandoned by the user after pressing play. Let’s try to break down what might be causing these issues.

Front-end is messy and complicated these days. That's why we publish articles, printed books and webinars with useful techniques to improve your work. Even better: Smashing Membership with a growing selection of front-end & UX goodies. So you get your work done, better and faster.

Explore Smashing Membership ↬ Video Playback Failure

Video playback failure accounted for 2.3% of all video plays. What could lead to this? In the HTTP Archive data, we see 0.3% of all video requests resulting in a 4xx or 5xx HTTP response — so some percentage fail to bad URLs or server misconfigurations. Another potential issue (that is not observed in the HTTP Archive data) are videos that are blocked by Geolocation (blocked based on the location of the viewer and the licensing of the provider to display the video in that locale).

Video Playback Abandonment

The Conviva report states that 11.5% of all video plays would play, but that the customer abandoned the playback before the video started playing. The issue here is that the video is not being delivered to the customer fast enough, and they give up. There are many studies on the mobile web where long delays cause abandonment of web pages, and it appears that the same effect occurs with video playback as well.

Research from Akamai shows that viewers will wait for 2 seconds, but for each subsequent second, 5.8% of viewers abandon the video.

Rate of abandonment over time (Large preview)

So what leads to video playback issues? In general, larger files take longer to download, so will delay playback. Let’s look at a few ways that one can speed up the playback of videos. To reduce the number of videos abandoned at startup, we should ‘slim’ down these files as best as possible, so they download (and begin playback) quickly.

MP4: Video Preload

To ensure fast playback on the web, one option is to preload the video onto the device in advance. That way, when your customer clicks ‘play’ the video is already downloaded, and playback will be fast. HTML offers a preload attribute with 3 possible options: auto, metadata and none.

preload = auto

When your video is delivered with preload="auto", the browser downloads the entire video file and stores it locally. This permits a large performance improvement for video startup, since the video is available locally on the device, and no network interference will slow the startup.

However, preload="auto" should only be used if there is a high probability that the video will be viewed. If the video is simply resident on your webpage, and it is downloaded each time, this will add a large data penalty to your mobile users, as well as increase your server/CDN costs for delivering the entire video to all of your users.

This website has a section entitled “Video Gallery” with several videos. Each video in this section has preload set to auto, and we can visualize their download in the WebPageTest waterfall as green horizontal lines:

Waterfall of video preload (Large preview)

There is a section called “Video Gallery”, and the files for this small section of the website account for 14.6M (83%) of the page download. The odds that one (of many) videos will be played is probably pretty low, and so utilizing preload="auto" only generates a lot of data traffic for the site.

Webpage data breakdown (Large preview)

In this case, it is unlikely that even one of these videos will be viewed, yet all of them are downloaded completely, adding 14.8MB of content to the mobile site (83% of the content on the page). For videos that are have a high probability of playback (perhaps >90% of page views result in video play) — preloading the entire video is a very good idea. But for videos that are unlikely to be played, preload="auto" will only cause extra tonnage of content through your servers and to your customer’s mobile (and desktop) devices.

preload="metadata"

When the preload="metadata" attribute is used, an initial segment of the video is downloaded. This allows the player to know the size of the video window, and to perhaps have a second or 2 of video downloaded for immediate playback. The browser simply makes a 206 (partial request) of the video content. By storing a small bit of video data on the device, video startup time is decreased, without a large impact to the amount of data transferred.

On Chrome, metadata is the default choice if no attribute is chosen.

Note: This can still lead to a large amount of video to be downloaded, if the video is large.

For example, on a mobile website with a video set at preload="metadata", we see just one request for video:

(Large preview)

And the request is a partial download, but it still results in 2.7 MB of video to be downloaded because the full video is 1080p, 150s long and 97 MB (we’ll talk about optimizing video size in the next sections).

KB usage with video metadata (Large preview)

So, I would recommend that preload="metadata" still only be used when there is a fairly high probability that the video will be viewed by your users, or if the video is small.

preload="none"

The most economical download option for videos, as no video files are downloaded when the page is loaded. This will potentially add a delay in playback, but will result in faster initial page load For sites with many videos on a single page, it may make sense to add a poster to the video window, and not download any of the video until it is expressly requested by the end user. All YouTube videos that are embedded on websites never download any video content until the play button is pressed, essentially behaving as if preload="none".

Preload Best Practice: Only use preload="auto" if there is a high probability that the video will be watched. In general, the use of preload="metadata" provides a good balance in data usage vs. startup time, but should be monitored for excessive data usage.

MP4 Video Playback Tips

Now that the video has started, how can we ensure that the video playback can be optimized to not stall and continue playing. Again, the trick is to make sure the video is as small as possible.

Let’s look at some tricks to optimize the size of video downloads. There are several dimensions of video that can be optimized to reduce the size of the video:

Audio

Video files are split into different “streams” — the most common being the video stream. The second most common stream is the audio track that syncs to the video. In some video playback applications, the audio stream is delivered separately; this allows for different languages to be delivered in s seamless manner.

If your video is played back in a silent manner (like a looping GIF, or a background video), removing the audio stream from the video is a quick and easy way to reduce the file size. In one example of a background video, the full file was 5.3 MB, but the audio track (which is never heard) was nearly 300 KB (5% of the file) By simple eliminating the audio, the file will be delivered quickly without wasting bytes.

42% of the MP4 files found on the HTTP Archive have no audio stream.

Best Practice: Remove the audio tracks from videos that are played silently.

Video Encoding

When encoding a video, there are options to reduce the video quality (number of pixels per frame, or the frames per second). Reducing a high-quality video to be suitable for the web is easy to do, and generally does not affect the quality delivered to your end users. This article is not long enough for an in depth discussion of all the various compression techniques available for video. In x264 and x265 encoders, there is a term called the Constant Rate Factor (CRF). Using a CRF of 23-28 will generally give a good compression/quality trade off, and is a great first start into the realm of video compression

Video Size

Video size can be affected by many dimensions: length, width, and height (you could probably include audio here as well).

Video Duration

The length of the video is generally not a feature that a web developer can adjust. If the video is going to playback for three minutes, it is going to playback for three minutes. In cases in which the video is exceptionally long, tools like preload="none" or streaming the video can allow for a smaller amount of data to be downloaded initially to reduce page load time.

Video Dimensions

18% of all video found in the HTTP Archive is identical on mobile and desktop. Those who have worked with responsive web design know how optimizing images for different viewports can drastically reduce load times since the size of the images is much smaller for smaller screens.

The same holds for video. A website with a 30 MB 2560×1226 background video will have a hard time downloading the video on mobile (probably on desktop, too!). Resizing the video drastically decreases the files size, and might even allow for three or four different background videos to be served:

Width Video (MB) 1226 30 1080 8.1 720 43 608 3.3 405 1.76

Now, unfortunately, browsers do not support media queries for video in HTML, meaning that this just does not work:

<video preload="auto" autoplay muted controls source sizes="(max-width:1400px 100vw, 1400px" srcset="small.mp4 200w, medium.mp4 800w, large.mp4 1400w" src="large.mp4" </video>

Therefore, we’ll need to create a small JS wrapper to deliver the videos we want to different screen sizes. But before we go there…

Downloading Video, But Hiding It From View

Another throwback to the early responsive web is to download full-size images, but to hide them on mobile devices. Your customers get all the delay for downloading the large images (and hit to mobile data plan, and extra battery drain, etc.), and none of the benefit of actually seeing the image. This occurs quite frequently with video on mobile. So, as we write our script, we can ensure that smaller screens never request the video that will not appear in the first place.

Retina Quality Videos

You may have different videos for different device screen densities. This can lead to added time to download the videos to your mobile customers. You may wish to prevent retina videos on smaller screen devices, or on devices with a limited network bandwidth, falling to back to standard quality videos for these devices. Tools like the Network Information API can provide you with the network throughput, and help you decide which video quality you’d like to serve to your customer.

Downloading Different Video Types Based On Device Size And Network Quality

We’ve just covered a few ways to optimize the delivery of movies to smaller screens, and also noted the inability of the video tag to choose between video types, so here is a quick JS snippet that will use the screen width to:

  • Not deliver video on screens below 500px;
  • Deliver small videos for screens 500-1400;
  • Deliver a larger sized video to all other devices.
<html><body> <div id="video"> </div> <div id="text"></div> <script> //get screen width and pixel ratio var width = screen.width; var dpr = window.devicePixelRatio; //initialise 2 videos — //“small” is 960 pixels wide (2.6 MB), large is 1920 pixels wide (10 MB) var smallVideo="http://res.cloudinary.com/dougsillars/video/upload/w_960/v1534228645/30s4kbbb_oblsgc.mp4"; var bigVideo = "http://res.cloudinary.com/dougsillars/video/upload/w_1920/v1534228645/30s4kbbb_oblsgc.mp4"; //TODO add logic on adding retina videos if (width<500){ console.log("this is a very small screen, no video will be requested"); } else if (width< 1400){ console.log("let’s call this mobile sized"); var videoTag = "\<video preload=\"auto\" width=\"100%\" autoplay muted controls src=\"" +smallVideo +"\"/\>"; console.log(videoTag); document.getElementById('video').innerHTML = videoTag; document.getElementById('text').innerHTML = "This is a small video."; } else{ var videoTag = "\<video preload=\"auto\" width=\"100%\" autoplay muted controls src=\"" +bigVideo +"\"/\>"; document.getElementById('video').innerHTML = videoTag; document.getElementById('text').innerHTML = "This is a big video."; } </script> </html></body>

This script divides user’s screens into three options:

  1. Under 500 pixels, no video is shown.
  2. Between 500 and 1400, we have a smaller video.
  3. For larger than 1400 pixel wide screens, we have a larger video.

Our page has a responsive video with two different sizes: one for mobile, and another for desktop-sized screens. Mobile users get great video quality, but the file is only 2.6 MB, compared to the 10MB video for desktop.

Animated GIFs

Animated GIFs are big files. While both aGIFs and video files compress the data through width and height dimensions, only video files have compression (on the often larger) time axis. aGIFs are essentially “flipping through” static GIF images quickly. This lack of compression adds a significant amount of data. Thankfully, it is possible to replace aGIFs with a looping video, potentially saving MBs of data for each request.

<video loop autoplay muted playsinline src="pseudoGif.mp4">

In Safari, there is an even fancier approach: You can place a looping mp4 in the picture tag, like so:

<picture> <source type="video/mp4" loop autoplay srcset="loopingmp4.mp4"> <source type="image/webp" srcset="animated.webp"> <src="animated.gif"> </picture>

In this case, Safari will play the animated GIF, while Chrome (and other browsers that support WebP) will play the animated WebP, with a fallback to the animated GIF. You can read more about this approach in Colin Bendell’s great post.

Third-Party Videos

One of the easiest ways to add video to your website is to simply copy/paste the code from a video sharing service and put it on your site. However, just like adding any third party to your site, you need to be vigilant about what kind of content is added to your page, and how that will affect page load. Many of these “simply paste this into your HTML” widgets add 100s of KB of JavaScript. Others will download the entire movie (think preload="auto"), and some will do both.

Third-Party Video Best Practice: Trust but verify. Examine how much content is added, and how much it affects your page load time. Also, the behavior might change, so track with your analytics regularly.

Streaming Startup

When a video stream is requested, the server supplies a manifest file to the player, listing every available stream (with dimensions and bitrate information). In HLS streaming, the player generally chooses the first stream in the list to begin playback. Therefore, the stream positioned first in the manifest file should be optimized for video startup on both mobile and desktop (or perhaps alternative manifest files should be delivered to mobile vs. desktop).

In most cases, the startup is optimized by using a lower quality stream to begin playback. Once the player downloads a few segments, it has a better idea of available throughput and can select a higher quality stream for later segments. As a user, you have likely seen this — where the first few seconds of a video looks very pixelated, but a few seconds into playback the video sharpens.

In examining 1,065 manifest files delivered to mobile devices from the HTTP Archive, we find that 59% of videos have an initial bitrate under 1.2 MBPS — and are likely to start streaming without any much delay at 1.6 MBPS 3G data rates. 11% use a bitrate between 1.2 and 1.6 MBPS — which may slow the startup on 3G, and 30% have a bitrate above 1.6 MBPS — and are unable to playback at this bitrate on a 3G connection. Based on this data, it appears that ~41% of all videos will not be able to sustain the initial bitrate on mobile — adding to startup delay, and possibly increased number of stalls during playback.

Initial bitrate for video streams (Large preview)

Streaming Startup Best Practice: Ensure your initial bitrate in the manifest file is one that will work for most of your customers. If the player has to change streams during startup, playback will be delayed and you will lose video views.

So, what happens when the video’s bitrate is near (or above) the available throughput? After a few seconds of download without a completed video segment ready for playback, the player stops the download and chooses a lower quality bitrate video, and begins the process again. The action of downloading a video segment and then abandoning leads to additional startup delay, which will lead to video abandonment.

We can visualize this by building video manifests with different initial bitrates. We test 3 different scenarios: starting with the lowest (215 KBPS), middle (600 KBPS), and highest bitrate (2.6 MBPS).

When beginning with the lowest quality video, playback begins at 11s. After a few seconds, the player begins requesting a higher quality stream, and the picture sharpens.

When starting with the highest bitrate (testing on a 3G connection at 1.6 MBPS), the player quickly realizes that playback cannot occur, and switches to the lowest bitrate video (215 KBPS). The video starts playing at 17s. There is a 6-second delay, and the video quality is the same low quality delivered to in the first test.

Using the middle-quality video allows for a bit of a tradeoff, the video begins playing at 13s (2 seconds slower), but is high quality from the start -and there is no jump from pixelated to higher quality video.

Best Practice for Video Startup: For fastest playback, start with the lowest quality stream. For longer videos, you might consider using a ‘middle quality’ stream at start to deliver sharp video at startup (with a slightly longer delay).

WebPage Test Thumbnails (Large preview)

WebPageTest results: Initial video stream is low, middle and high (from top to bottom). The video starts the fastest with the lowest quality video. It is important to note that the high quality start video at 17s is the same quality as the low quality start at 11s.

Streaming: Continuing Playback

When the video player can determine the optimal video stream for playback and the stream is lower than the available network speed, the video will playback with no issues. There are tricks that can help ensure that the video will deliver in an optimal manner. If we examine the following manifest entry:

#EXT-X-STREAM-INF:BANDWIDTH=912912,PROGRAM-ID=1,CODECS="avc1.42c01e,mp4a.40.2",RESOLUTION=640x360,SUBTITLES="subs" video/600k.m3u8

The information line reports that this stream has a 913 KBPS bitrate, and 640×360 resolution. If we look at the URL that this line points to, we see that it references a 600k video. Examining the video files shows that the video is 600 KBPS, and the manifest is overstating the bitrate.

Overstating The Video Bitrate
  • PRO
    Overstating the bitrate will ensure that when the player chooses a stream, the video will download faster than expected, and the buffer will fill up faster than expected, reducing the possibility of a stall.
  • CON
    By overstating the bitrate, the video delivered will be a lower quality stream. If we look at the entire list of reported vs. actual bitrates:
Reported (KBS) Actual Resolution 913 600 640x360 142 64 320x180 297 180 512x288 506 320 512x288 689 450 412x288 1410 950 853x480 2090 1500 1280x720

For users on a 1.6 MBPS connection, the player will choose the 913 KBPS bitrate, serving the customer 600 KBPS video. However, if the bitrates had been reported accurately, the 950 KBPS bitrate would be used, and would likely have streamed with no issues. While the choices here prevent stalls, they also lower the quality of the delivered video to the consumer.

Best Practice: A small overstatement of video bitrate may be useful to reduce the number of stalls in playback. However, too large a value can lead to reduced quality playback.

Test the Neilsen video in the browser, and see if you can make it jump back and forth.

Conclusion

In this post, we’ve walked through a number of ways to optimize the videos that you present on your websites. By following the best practices illustrated in this post:

  1. preload="auto"
    Only use if there is a high probability that this video will be watched by your customers.
  2. preload="metadata"
    Default in Chrome, but can still lead to large video file downloads. Use with caution.
  3. Silent Videos (looping GIFs or background videos)
    Strip out the audio channel
  4. Video Dimensions
    Consider delivering differently sized video to mobile over desktop. The videos will be smaller, download faster, and your users are unlikely to see the difference (your server load will go down too!)
  5. Video Compression
    Don’t forget to compress the videos to ensure that they are delivered
  6. Don’t ‘hide’ videos
    If the video will not be displayed — don’t download it.
  7. Audit your third-party videos regularly
  8. Streaming
    Start with a lower quality stream to ensure fast startup. (For longer play videos, consider a medium bitrate for better quality at startup)
  9. Streaming
    It’s OK to be conservative on bitrate to prevent stalling, but go too far, and the streams will deliver a lower quality video.

You will find that the video on your page is streamlined for optimal delivery and that your customers will not only delight in the video you present but also enjoy a faster page load time overall.

(dm, ra, il)
Categories: Around The Web

Video Playback On The Web: The Current State Of Video (Part 1)

Smashing Magazine - Wed, 10/24/2018 - 6:30am
Video Playback On The Web: The Current State Of Video (Part 1) Video Playback On The Web: The Current State Of Video (Part 1) Doug Sillars 2018-10-24T13:30:24+02:00 2018-11-14T10:02:42+00:00

Usage of video on the web is increasing as devices and networks become faster and more capable of handling video content. Research shows that sites with video increase engagement by 80%. E-Commerce sites with video have higher conversions than sites without video.

But adding video can come at a cost. Videos (being larger files) add to the page load time, and performance research shows that slower pages have the opposite effect of lower customer engagement and conversions. In this aticle, I’ll examine the important metrics to balance performance and video playback on the web, look at how video is being used today, and provide best practices on delivering video on the web.

One of the first steps to improve customer satisfaction is to speed up the load time of a page. Google has shown that mobile pages that take over three seconds to load lose 53% of their audience to abandonment. Other studies find that on improving site performance, usage and sales increase.

Adding video to a website will increase engagement, but it can also dramatically slow down the load time, so it is clear that a balance must be found between adding videos to your site and not impacting the load time too greatly.

Recommended reading: Front-End Performance Checklist 2018 [PDF, Apple Pages]

Video On The Web Today

To examine the state of video on the web today, I’ll use data from the HTTP Archive. The HTTP Archive uses WebPageTest to scan the performance of 1.2 million mobile and desktop websites every two weeks, and then stores the data in Google BigQuery.

Typically just the main page of each domain is checked (meaning www.cnn.com is run, but www.cnn.com/politics is not). This data can help us understand how the usage of video on the web affects the performance of websites. Mobile tests are run on an emulated Motorola G4 with a 3G internet connection (1.6 MBPS down, 768 KBPS up, 300 ms RTT), and desktop tests run Chrome on a cable connection (5 MBPS down, 1 MBPS up, 28ms RTT). I’ll be using the data set from 1 August 2018.

Front-end is messy and complicated these days. That's why we publish articles, printed books and webinars with useful techniques to improve your work. Even better: Smashing Membership with a growing selection of front-end & UX goodies. So you get your work done, better and faster.

Explore Smashing Membership ↬ Sites That Download Video

As a first step to study sites with video, we should look at sites that download video files when the page loads. There are 35k mobile sites and 55k desktop sites with video file downloads in the HTTP Archive data set (that’s 3% of all mobile sites and 4.5% of all desktop sites). Comparing desktop to mobile, we find that 30k of these sites have video on both mobile and desktop (leaving ~5,800 sites on mobile with no video on the desktop).

Mobile and Desktop Sites with Video (Large preview)

The median mobile page with video weighs in at a hefty 7 MB (583% larger than 1.2 MB found for the median mobile site). This increase is not fully accounted for by video alone (2.5 MB). As sites with video tend to be more feature rich and visually engaging, they also use more images (the median site has over 1 MB more), CSS, and Javascript. The table below also shows that the median SpeedIndex (a measurement of how quickly content appears on the screen) for sites with video is 3.7s slower than a typical mobile site, taking a whopping 11.5 seconds to load.

SpeedIndex Bytes Total Bytes Video Bytes CSS Bytes Images Bytes JS Video 11544 6,963,579 2,526,098 80,327 1,596,062 708,978 all sites 7780 1,201,802 0 40,648 449,585 336,973

This clearly shows that sites that are more interactive and have video content take (on average) longer to load that sites without video. But can we speed up video delivery? What else can we learn from the data at hand?

Video Hosting

When examining video delivery, are the files being served from a CDN or video provider, or are developers hosting the videos on their own servers? By examining the domain of the videos delivered on mobile, we find that 12,163 domains are used to deliver video, indicating that ~49% of sites are serving their own video files. If we stack rank the domains by frequency, we are able to determine the most common video hosting solutions:

Video Doman cnt % fbcdn.net 116788 67% akamaihd.net 11170 6% googlevideo.com 10394 6% cloudinary.com 3170 2% amazonaws.com 1939 1% cloudfront.net 1896 1% pixfs.net 1853 1% akamaized.net 1573 1% tedcdn.com 1507 1% contentabc.com 1507 1% vimeocdn.ccom 1373 1% dailymotion.com 1337 1% teads.tv 1022 1% youtube.com 1007 1% adstatic.com 998 1%

Top CDNs and domains Facebook, Akamai, Google, Cloudinary, AWS, and Cloudfront lead the way, which is not surprising. However, it is interesting to see YouTube and Vimeo so far down in the list, as they are two of the most popular video sharing sites.

Let’s look into YouTube, Vimeo and Facebook video delivery:

YouTube Video Counts

By default, pages with a YouTube video embedded do not actually download any video files — just scripts and a placeholder image, so they do not show up in a querly looking for sites with video downloads. One of the Javascript downloads for the YouTube Video player is www-embed-player.js. Searching for this file, we find 69k instances on 66,647 mobile sites. These sites have a median SpeedIndex of 10,700, and data usage of 3.31MB — better than sites with video downloaded, but still slower than sites with no video at all. The increase in data is directly associated with more images and Javascript (as shown below).

Speedindex Bytes Total Bytes Video Bytes CSS Bytes Images Bytes JS Video 11544 6,963,579 2,526,098 80,327 1,596,062 708,978 All sites 7780 1,201,802 0 40,648 449,585 336,973 YouTube script 10700 3,310,000 0 126,314 1,733,473 1,005,758 Vimeo Video Counts

There are 14,148 requests for Vimeo videos in HTTP Archive for Video playback. I see only 5,848 requests for the player.js file (in the format https://f.vimeocdn.com/p/3.2.0/js/player.js — implying that perhaps there are many videos on one page, or perhaps another location for the video player file. There are 17 different versions of the player present into HTTP Archive, with the most popular being 3.1.5 and 3.1.4:

URL cnt https://f.vimeocdn.com/p/3.1.5/js/player.js 1832 https://f.vimeocdn.com/p/3.1.4/js/player.js 1057 https://f.vimeocdn.com/p/3.1.17/js/player.js 730 https://f.vimeocdn.com/p/3.1.8/js/player.js 507 https://f.vimeocdn.com/p/3.1.10/js/player.js 432 https://f.vimeocdn.com/p/3.1.15/js/player.js 352 https://f.vimeocdn.com/p/3.1.19/js/player.js 153 https://f.vimeocdn.com/p/3.1.2/js/player.js 117 https://f.vimeocdn.com/p/3.1.13/js/player.js 105

There does not appear to be any performance gain for any Vimeo Library — all of the pages have similar load times.

Note: Using www-embed-player.js for YouTube or https://f.vimeocdn.com/p/*/js/player.js for Vimeo are good fingerprints for browsers with a clean cache, but if the customer has previously browsed a site with an embedded video, this file might already be in the browser cache, and thus will not be re-requested. But, as Andy Davies recently noted, this is not a safe assumption to make.

Facebook Video Counts

It is surprising that in the HTTP Archive data, 67% of all video requests are from Facebook’s CDN. It turns out that on Chrome, 3rd party Facebook widgets download 30% of all videos posted inside the widget (This effect does not occur in Safari or in Firefox.). It turns out that a 3rd party widget added with just a few lines of code is responsible for 57% of all the video seen in the HTTP Archive.

Video File Types

The majority of videos on pages tested are Mp4s. If we look at all the videos downloaded (excluding those from Facebook), we get the following view:

File extension Video count % .mp4 48,448 53% .ts 18,026 20% .webm 3,946 4% 14,926 16% .m4s 2,017 2% .mpg 1,431 2% .mov 441 0% .m4v 407 0% .swf 251 0%

Of the files with no extension — 10k are googlevideo.com files.

What can we learn about these files? Let’s look each file type to learn more about the content being delivered.

I used FFPROBE to query the 34k unique MP4 files, and obtained data for 14,700 videos (many of the videos had changed or been removed in the 3 weeks from HTTP Archive capture to analysis).

MP4 Video Data

Of the 14.7k videos in the dataset, 8,564 have audio tracks (58%). Shorter videos that autoplay or videos that play in the background do not require audio, so stripping the audio track is a great way to reduce the file size (and speed the delivery) of your videos.

The next most important aspect to quickly downloading a video are the dimensions. The larger the dimensions (and in the case of video, there are three dimensions to consider: width × height × time), the larger the video file will be.

MP4 Video Duration

Most of the 14k videos studied are short: the median (50th percentile) duration is 21s. However, 10% of the videos surveyed are over 2 minutes in duration. Use cases here will, of course, be divided, but for short video loops, or background videos — shorter videos will use less data, and download faster.

Distribution of Video Duration (Large preview) MP4 Video Width And Height

The dimensions of the video on the screen decide how many pixels each frame will have to use. The chart below shows the various video widths that are being served to the mobile device. (As a note, the Moto G4 has a screen size of 1080×1920, and the pages are all viewed in portrait mode).

Video Counts by Width (Large preview)

As the data shows, the two most utilized video widths are significantly larger than the G4 screen (when held in portrait mode). A full 49% of all videos are served with a width greater than 1080 pixels wide. The Alcatel 1x, a new Android Go device, has a 480×960-pixel screen. 77% of the videos delivered in the sample set are larger than 480 pixels wide.

As dimensions of videos decrease, so does the files size (and thus time to deliver the video). This is the primary reason to resize videos.

Why are these videos so large? If we correlate the videos served on mobile and desktop, we find that 18% of videos served on mobile are the same videos served on the desktop. This is a ‘problem’ solved for images years ago with responsive images. By serving differently sized videos to different sized devices, we can ensure that beautiful videos are served, but at a size and dimension that makes sense for the device.

MP4 Video Bitrate

The bitrate of the video delivered to the device plays a large effect on how well the video will play back. The HTTP Archive tests are run on a 3G connection at 1.6 MBPS download speed. To playback (without stalling) the download has to be faster than playback. Let’s provide 80% of the available bitrate to video files (1.3 MBPS). 47% of the videos in the sample set have a bitrate over 1.3 MBPS, meaning that when these videos are played on a 3G connection, they are more likely to stall — leading to unhappy customers. 27% of videos have a bitrate higher than 2.5 MBPS, 10% are higher than 5 MBPS, and 35 of videos served to mobile devices have a bitrate > 10 MBPS. These larger videos are unlikely to play without stalling on many connections — fixed or mobile.

Observed Video Bitrates (Large preview) What Leads To Higher Bitrates

Larger videos tend to carry a larger bitrate, as larger dimensioned videos require a lot more data to populate the additional pixels on the device. Cross referencing the bitrate of each video to the width confirms this: videos with width 1280 (orange) and 1920 (gray) have a much broader distribution of bitrates (more data points to the right in the chart). The column marked in yellow denotes the 136 videos with width 1920, and a bitrate between 10-11 MBPS.

Bitrate Vs. Video Width (Large preview)

If we visualize only the videos over 1.6 MBPS, it becomes clear that the higher screen resolutions (1280 and 1920) are responsible for the higher bitrate videos.

High Bitrate and Video Width (Large preview) MP4: HTTP vs. HTTPS

HTTP2 has redefined content delivery with multiplexed connections — where just one connection per server is required. For large files like video, does HTTP2 provide a meaningful improvement to content delivery? If we look at the stats from the HTTP Archive:

HTTP1 vs HTTTP2 (Large preview)

Omitting the 116k Facebook videos (all sent via HTTP2), we see that it is about a 50:50 split between HTTP 1.1 and HTTP2. However, HTTP1.1 can utilize HTTPS, and when we filter for HTTPS usage, we find that 81% of video streams are sent via HTTPS, with HTTP2 being used slightly more than HTTPS1.1 (41%:36%)

HTTP vs. HTTP2 and secure (Large preview)

As you can see, comparing the speed of HTTP and HTTP2 video delivery is a work in progress.

HLS Video Streaming

Video streaming using adaptive bitrate is an ideal way to deliver video to the end user. Multiple versions of each video are built with different dimensions and bitrates. The list of available streams is presented to the playback device, and the video player on the device can choose the most appropriate stream based on the size of the device screen and the available network conditions. There are 1,065 manifest files (and 14k video stream files) in the HTTP Archive data set that I examined.

Video Stream Playback

One key metric in video streaming is to have the video start as quickly as possible. While the manifest file has a list of available streams, the player has no idea the available bandwidth of the network at the beginning of playback. To begin streaming, and because the player has to pick a stream, it typically chooses the first one in the list. In order to facilitate a fast video startup, it is important to place the correct stream at the top of your manifest file.

Note: Utilizing the Chrome Network Info API to generate manifest files on the fly might be a good way to quickly optimize video content at startup.

One way to ensure that the video starts quickly is to start with the lowest quality video segment, as the download will be the fastest. The initial video quality may be pixelated, but as the player better understands the network quality, it can quickly adjust to a more appropriate (hopefully higher quality) video stream. With that in mind, let’s look at the initial stream bitrates in the HTTP Archive.

Initial Bitrate for video streams (Large preview)

The red line in the above chart denotes 1.5 MBPS (recall that mobile tests are run at 1.6 MBPS, and not only video content is being downloaded). We see 30.5% of all of the streams (everything to the left of the line) start with an initial bitrate higher than 1.5 MBPS (and are thus unlikely to playback on a 3G connection) 17% start above 2 MBPS.

So what happens when video download is slower than the actual playback of the video? Initially, the player will attempt to download the (too) large bitrate files, but based on the download speed, will realise the problem. The player will then switch to downloading a lower bitrate video, so that download is faster than playback. The problem is that the initial download attempt takes time, and adding a delay to video playback start leads to customer abandonment.

We can also look at the most common bitrates of .ts files (the files that have the video content), to see what bitrates end up being downloaded on mobile. This data includes the initial bitrates, and any subsequent file downloaded during the WebPageTest run:

Observed Mobile Bitrates (Large preview)

We can see two major groupings of video streaming bitrates (100-300 KBPS, and a broader peak from 300-1,000 MBPS). This is where we would expect streams to appear, given that the network speed is capped at 1.6 MBPS.

Comparing the data to the desktop, Mobile clearly is higher at the lower bitrates, and desktop streams have high peaks in the 500-600 and 800-900 KBPS ranges, that are not seen in mobile.

Observed mobile and Desktop streaming bitrates (Large preview) Observed bitrates, mobile, desktop compared to initial bitrate (Large preview)

When we compare the initial bitrates observed (blue) with the actual files downloaded, it is very clear that for mobile the bitrate generally decreases during stream playback, indicating that lowering the initial bitrate for video streams might improve the performance of video startup and prevent stalls in early playback of the video. Desktop video also appears to decrease, but it is also possible that some video move to higher playback speeds.

Conclusion

Video content on the web increases customer engagement and satisfaction. Pages that load quickly have the same effect. The addition of video to your website will slow down the page rendering time, necessitating a balance between overall page load and video content. To reduce the size of your video content, ensure that you have versions appropriately sized for mobile device dimensions, and use shorter videos when possible.

If playback of your videos is not essential, follow the path of YouTube and Vimeo — download all the required pieces to be ready for playback, but don’t actually download any video segments until the user presses play. Finally — if you are streaming video — start with the lowest quality setting to ensure a fast video playback.

In my next post on video, I will take these general findings, and dig deeper into how to resolve potential issues with examples.

(dm, ra, il)
Categories: Around The Web

Splicing HTML’s DNA With CSS Attribute Selectors

Smashing Magazine - Tue, 10/23/2018 - 7:00am
Splicing HTML’s DNA With CSS Attribute Selectors Splicing HTML’s DNA With CSS Attribute Selectors John Rhea 2018-10-23T14:00:11+02:00 2018-11-13T09:38:54+00:00

For most of my career, attribute selectors have been more magic than science. I’d stare, gobsmacked, at the CSS for outputting a link in a print style sheet, understanding nothing. I’d dutifully copy, and paste it into my print stylesheet then run off to put out whatever project was the largest burning trash heap.

But you don’t have to stare slack-jawed at CSS attribute selectors anymore. By the end of this article, you’ll use them to run diagnostics on your site, fix otherwise unsolvable problems, and generate technologic experiences so advanced they feel like magic. You may think I’m promising too much and you’re right, but once you understand the power of attribute selectors, you might feel like exaggerating yourself.

On the most basic level, you put an HTML attribute in square brackets and call it an attribute selector like so:

[href] { color: chartreuse; }

The text of any element that has an href and doesn’t have a more specific selector will now magically turn chartreuse. Attribute selector specificity is the same as classes.

Note: For more on the cage match that is CSS specificity, you can read “CSS Specificity: Things You Should Know” or if you like Star Wars: “CSS Specificity Wars”.

But you can do far more with attribute selectors. Just like your DNA, they have built-in logic to help you choose all kinds of attribute combinations and values. Instead of only exact matching the way a tag, class, or id selector would, they can match any attribute and even string values within attributes.

Front-end is messy and complicated these days. That's why we publish articles, printed books and webinars with useful techniques to improve your work. Even better: Smashing Membership with a growing selection of front-end & UX goodies. So you get your work done, better and faster.

Explore Smashing Membership ↬ Attribute Selection

Attribute selectors can live on their own or be more specific, i.e. if you need to select all div tags that had a title attribute.

div[title]

But you could also select the children of divs that have a title by doing the following:

div [title]

To be clear, no space between them means the attribute is on the same element (just like an element and class without a space), and a space between them means a descendant selector, i.e. selecting the element’s children who have the attribute.

You can get far more granular in how you select attributes including the values of attributes.

div[title="dna"]

The above selects all divs with an exact title of “dna”. A title of “dna is awesome” or “dnamutation” wouldn’t be selected, though there are selector algorithms that handle each of those cases (and more). We’ll get to those soon.

Note: Quotation marks are not required in attribute selectors in most cases, but I will use them because I believe it increases clarity and ensures edge cases work appropriately.

If you wanted to select “dna” out of a space separated list like “my beautiful dna” or “mutating dna is fun!” you can add a tilde or “squiggly,” as I like to call it, in front of the equal sign.

div[title~="dna"]

You can select titles such as “dontblamemeblamemydna” or “his-stupidity-is-from-upbringing-not-dna” then you can use the dollar sign $ to match the end of a title.

[title$="dna"]

To match the front of an attribute value such as titles of “dnamutants” or “dna-splicing-for-all” use a caret.

[title^="dna"]

While having an exact match is helpful it might be too tight of a selection, and the caret front match might be too wide for your needs. For instance, you might not want to select a title of “genealogy”, but still select both “gene” and “gene-data”. The pipe character (or vertical bar) is just that; it does an exact match, but includes when the exact match is followed by a dash.

[title|="gene"]

Lastly, there’s a full search attribute operator that will match any substring.

[title*="dna"]

But use it wisely as the above will match “I-like-my-dna-like-my-meat-rare” as well as “edna”, “kidnapping”, and “echidnas” (something Edna really shouldn’t do.)

What makes these attribute selectors even more powerful is that they’re stackable — allowing you to select elements with multiple matching factors.

But you need to find the a tag that has a title and has a class ending in “genes”? Here’s how:

a[title][class$="genes"]

Not only can you select the attributes of an HTML element you can also print their mutated genes using pseudo-“science” (meaning pseudo-elements and the content declaration).

<span class="joke" title="Gene Editing!">What’s the first thing a biotech journalist does after finishing the first draft of an article?</span> .joke:hover:after { content: "Answer:" attr(title); display: block; }

The code above will show the answer to one of the worst jokes ever written on hover (yes, I wrote it myself, and, yes, calling it a “joke” is being generous).

The last thing to know is that you can add a flag to make the attribute searches case insensitive. You add an i before the closing square bracket.

[title*="DNA" i]

And thus it would match “dna”, “DNA”, “dnA”, and any other variation.

The only downside to this is that the i only works in Firefox, Chrome, Safari, Opera and a smattering of mobile browsers.

Now that we’ve seen how to select with attribute selectors, let’s look at some use cases. I’ve divided them into two categories: General Uses and Diagnostics.

General Uses Style By Input Type

You can style input types differently, e.g. email vs. phone.

input[type="email"] { color: papayawhip; } input[type="tel"] { color: thistle; } Display Telephone Links

You can hide a phone number at certain sizes and display a phone link instead to make calling easier on a phone.

span.phone { display: none; } a[href^="tel"] { display: block; } Internal vs. External Links, Secure vs. Insecure

You can treat internal and external links differently and style secure links differently from insecure links.

a[href^="http"]{ color: bisque; } a:not([href^="http"]) { color: darksalmon; } a[href^="http://"]:after { content: url(unlock-icon.svg); } a[href^="https://"]:after { content: url(lock-icon.svg); } Download Icons

One attribute HTML5 gave us was “download” which tells the browser to, you guessed it, download that file rather than trying to open it. This is useful for PDFs and DOCs you want people to access but don’t want them to open right now. It also makes the workflow for downloading lots of files in a row easier. The downside to the download attribute is that there’s no default visual that distinguishes it from a more traditional link. Often this is what you want, but when it’s not, you can do something like the below.

a[download]:after { content: url(download-arrow.svg); }

You could also communicate file types with different icons like PDF vs. DOCX vs. ODF, and so on.

a[href$="pdf"]:after { content: url(pdf-icon.svg); } a[href$="docx"]:after { content: url(docx-icon.svg); } a[href$="odf"]:after { content: url(open-office-icon.svg); }

You could also make sure that those icons were only on downloadable links by stacking the attribute selector.

a[download][href$="pdf"]:after { content: url(pdf-icon.svg); } Override Or Reapply Obsolete/Deprecated Code

We’ve all come across old sites that have outdated code, but sometimes updating the code isn’t worth the time it’d take to do it on six thousand pages. You might need to override or even reapply a style implemented as an attribute before HTML5.

<div bgcolor="#000000" color="#FFFFFF">Old, holey genes</div> div[bgcolor="#000000"] { /*override*/ background-color: #222222 !important; } div[color="#FFFFFF"] { /*reapply*/ color: #FFFFFF; } Override Specific Inline Styles

Sometimes you’ll come across inline styles that are gumming up the works, but they’re coming from code outside your control. It should be said if you can change them that would be ideal, but if you can’t, here’s a workaround.

Note: This works best if you know the exact property and value you want to override, and if you want it overridden wherever it appears.

For this example, the element’s margin is set in pixels, but it needs to be expanded and set in ems so that the element can re-adjust properly if the user changes the default font size.

<div style="color: #222222; margin: 8px; background-color: #EFEFEF;"Teenage Mutant Ninja Myrtle</div> div[style*="margin: 8px"] { margin: 1em !important; }

Note: This approach should be used with extreme caution as if you ever need to override this style you’ll fall into an !important war and kittens will die.

Showing File Types

The list of acceptable files for a file input is invisible by default. Typically, we’d use a pseudo element for exposing them and, though you can’t do pseudo elements on most input tags (or at all in Firefox or Edge), you can use them on file inputs.

<input type="file" accept="pdf,doc,docx"> [accept]:after { content: "Acceptable file types: " attr(accept); } HTML Accordion Menu

The not-well-publicized details and summary tag duo are a way to do expandable/accordion menus with just HTML. Details wrap both the summary tag and the content you want to display when the accordion is open. Clicking on the summary expands the detail tag and adds an open attribute. The open attribute makes it easy to style an open details tag differently from a closed details tag.

<details> <summary>List of Genes</summary> Roddenberry Hackman Wilder Kelly Luen Yang Simmons </details> details[open] { background-color: hotpink; } Printing Links

Showing URLs in print styles led me down this road to understanding attribute selectors. You should know how to construct it yourself now. You simply select all a tags with an href, add a pseudo-element, and print them using attr() and content.

a[href]:after { content: " (" attr(href) ") "; } Custom Tooltips

Creating custom tooltips is fun and easy with attribute selectors (okay, fun if you’re a nerd like me, but easy either way).

Note: This code should get you in the general vicinity, but may need some tweaks to the spacing, padding, and color scheme depending on your site’s environment and whether you have better taste than me or not.

[title] { position: relative; display: block; } [title]:hover:after { content: attr(title); color: hotpink; background-color: slateblue; display: block; padding: .225em .35em; position: absolute; right: -5px; bottom: -5px; } AccessKeys

One of the great things about the web is that it provides many different options for accessing information. One rarely used attribute is the ability to set an accesskey so that that item can be accessed directly through a key combination and the letter set by accesskey (the exact key combination depends on the browser). But there’s no easy way to know what keys have been set on a website.

The following code will show those keys on :focus. I don’t use on hover because most of the time people who need the accesskey are those who have trouble using a mouse. You can add that as a second option, but be sure it isn’t the only option.

a[accesskey]:focus:after { content: " AccessKey: " attr(accesskey); } Diagnostics

These options are for helping you identify issues either during the build process or locally while trying to fix issues. Putting these on your production site will make errors stick out to your users.

Audio Without Controls

I don’t use the audio tag too often, but when I do use it, I often forget to include the controls attribute. The result: nothing is shown. If you’re working in Firefox, this code can help you suss out if you’ve got an audio element hiding or if syntax or some other issue is preventing it from appearing (it only works in Firefox).

audio:not([controls]) { width: 100px; height: 20px; background-color: chartreuse; display: block; } No Alt Text

Images without alt text are a logistics and accessibility nightmare. They’re hard to find by just looking at the page, but if you add this they’ll pop right out.

Note: I use outline instead of border because borders could add to the element’s width and mess up the layout. outline does not add width.

img:not([alt]) { /* no alt attribute */ outline: 2em solid chartreuse; } img[alt=""] { /* alt attribute is blank */ outline: 2em solid cadetblue; } Asynchronous Javascript Files

Web pages can be a conglomerate of content management systems and plugins and frameworks and code that Ted (sitting three cubicles over) wrote on vacation because the site was down and he fears your boss. Figuring out what JavaScript loads asynchronously and what doesn’t can help you focus on where to enhance page performance.

script[src]:not([async]) { display: block; width: 100%; height: 1em; background-color: red; } script:after { content: attr(src); } Javascript Event Elements

You can also highlight elements that have a JavaScript event attribute to refactor them into your JavaScript file. I’ve focused on the OnMouseOver attribute here, but it works for any of the JavaScript event attributes.

[OnMouseOver] { color: burlywood; } [OnMouseOver]:after { content: "JS: " attr(OnMouseOver); } Hidden Items

If you need to see where your hidden elements or hidden inputs live you can show them with:

[hidden], [type="hidden"] { display: block; }

But with all these amazing capabilities you think there must be a catch. Surely attribute selectors must only work while flagged in Chrome or in the nightly builds of Fiery Foxes on the Edge of a Safari. This is just too good to be true. And, unfortunately, there is a catch.

If you want to work with attribute selectors in that most beloved of browsers — that is, IE6 — you won’t be able to. (It’s okay; let the tears fall. No judgments.) Pretty much everywhere else you’re good to go. Attribute selectors are part of the CSS 2.1 spec and thus have been in browsers for the better part of a decade.

And so these selectors should no longer be magical to you but revealed as a sufficiently advanced technology. They are more science than magic, and now that you know their deepest secrets, it’s up to you. Go forth and work mystifying wonders of science upon the web.

(dm, ra, yk, il)
Categories: Around The Web

Monthly Web Development Update 10/2018: The Hurricane Web, End-To-End-Integrity, And RAIL

Smashing Magazine - Fri, 10/19/2018 - 8:19am
Monthly Web Development Update 10/2018: The Hurricane Web, End-To-End-Integrity, And RAIL Monthly Web Development Update 10/2018: The Hurricane Web, End-To-End-Integrity, And RAIL Anselm Hannemann 2018-10-19T15:19:58+02:00 2018-11-12T10:25:16+00:00

With the latest studies and official reports out this week, it seems that to avoid an irreversible climate change on Planet Earth, we need to act drastically within the next ten years. This rose a couple of doubts and assumptions that I find worth writing about.

One of the arguments I hear often is that we as individuals cannot make an impact and that climate change is “the big companies’ fault”. However, we as the consumers are the ones who make the decisions what we buy and from whom, whose products we use and which ones we avoid. And by choosing wisely, we can make a change. By talking to other people around you, by convincing your company owner to switch to renewable energy, for example, we can transform our society and economy to a more sustainable one that doesn’t harm the planet as much. It will be a hard task, of course, but we can’t deny our individual responsibility.

Maybe we should take this as an occasion to rethink how much we really need. Maybe going out into nature helps us reconnect with our environment. Maybe building something from hand and with slow methods, trying to understand the materials and their properties, helps us grasp how valuable the resources we currently have are — and what we would lose if we don’t care about our planet now.

News
  • Chrome 70 is here with Desktop Progressive Web Apps on Windows and Linux, public key credentials in the Credential Management API, and named Workers.
  • Postgres 11 is out and brings more robustness and performance for partitioning, enhanced capabilities for query parallelism, Just-in-Time (JIT) compilation for expressions, and a couple of other useful and convenient changes.
  • As the new macOS Mojave and iOS 12 are out now, Safari 12 is as well. What’s new in this version? A built-in password generator, a 3D and AR model viewer, icons in tabs, web pages on the latest watch OS, new form field attribute values, the Fullscreen API for iOS on iPads, font collection support in WOFF2, the font-display loading CSS property, Intelligent Tracking Prevention 2.0, and a couple of security enhancements.
  • Google’s decision to force users to log into their Google account in the browser to be able to access services like Gmail caused a lot of discussions. Due to the negative feedback, Google promptly announced changes for v70. Nevertheless, this clearly shows the interests of the company and in which direction they’re pushing the app. This is unfortunate as Chrome and the people working on that project shaped the web a lot in the past years and brought the ecosystem “web” to an entirely new level.
  • Microsoft Edge 18 is out and brings along the Web Authentication API, new autoplay policies, Service Worker updates, as well as CSS masking, background blend, and overscroll.

Web forms are such an important part of the web, but we design them poorly all the time. The brand-new “Form Design Patterns” book is our new practical guide for people who design, prototype and build all sorts of forms for digital services, products and websites. The eBook is free for Smashing Members.

Check the table of contents ↬ General
  • Max Böck wrote about the Hurricane Web and what we can do to keep users up-to-date even when bandwidth and battery are limited. Interestingly, CNN and NPR provided text-only pages during Hurricane Florence to serve low traffic that doesn’t drain batteries. It would be amazing if we could move the default websites towards these goals — saving power and bandwidth — to improve not only performance and load times but also help the environment and make users happier.
UI/UX Shawn Parks shares the lessons he learned from redesigning his portfolio every year. (Image credit) Accessibility Tooling Privacy
  • Guess what? Our simple privacy-enhancing tools that delete cookies are useless as this article shows. There are smarter ways to track a user via TLS session tracking, and we don’t have much power to do anything against it. So be aware that someone might be able to track you regardless of how many countermeasures you have enabled in your browser.
  • Josh Clark’s comment on university research about Google’s data collection is highlighting the most important parts about how important Android phone data is to Google’s business model and what type of information they collect even when your smartphone is idle and not moving location.
Security Cloudflare’s IPFS gateway allows a website to be end-to-end secure while maintaining the performance and reliability benefits of being served from their edge network. (Image credit) Web Performance The four parts of the RAIL performance model: Response, Animation, Idle, Load. (Image credit) HTML & SVG JavaScript
  • Willian Martins shares the secrets of JavaScript’s bind() function, a widely unknown operator that is so powerful and allows us to invoke this from somewhere else into named, non-anonymous functions. A different way to write JavaScript.
  • Everyone knows what the “9am rush hour” means. Paul Lewis uses the term to rethink how we build for the web and why we should try to avoid traffic jams on the main thread of the browser and outsource everything that doesn’t belong to the UI into separate traffic lanes instead.
CSS Did you know you can use negative grid line numbers to position Grid items with CSS? (Image credit) Work & Life Going Beyond…
  • In the Netherlands, there’s now a legal basis that prescribes CO2 emissions to be cut by 25% by 2020 (that’s just a bit more than one year from now). I love the idea and hope other countries will be inspired by it — Germany, for example, which currently moves its emission cut goals farther and farther into the future.
  • David Wolpert explains why computers use so much energy and how we could make them vastly more efficient. But for that to happen, we need to understand the thermodynamics of computing better.
  • Turning down twenty billion dollars is cool. Of course, it is. But the interesting point in this article about the Whatsapp founder who just told the world how unhappy he is having sold his service to Facebook is that it seems that he believed he could keep the control over his product.

One more thing: I’m very grateful for all of you who helped raise my funding level for the Web Development Reading List to 100% this month. I never got so much feedback from you and so much support. Thank you! Have a great month!

—Anselm

(cm)

Categories: Around The Web

Reasons Your Mobile App Retention Rate Might Be So Low

Smashing Magazine - Thu, 10/18/2018 - 7:00am
Reasons Your Mobile App Retention Rate Might Be So Low Reasons Your Mobile App Retention Rate Might Be So Low Suzanne Scacca 2018-10-18T14:00:01+02:00 2018-11-09T10:44:31+00:00

In business, there’s a lot of talk about generating customer loyalty and retaining the business of good customers. Mobile apps aren’t all that different when you think about it.

While the number of installs may signal that an app is popular with users initially, it doesn’t tell the whole story. In order for an app to be successful, it must have loyal subscribers that make use of the app as it was intended. Which is where the retention rate enters the picture.

In this article, I want to explore what a good retention rate looks like for mobile apps. I’ll dig into the more common reasons why mobile apps have low retention rates and how those issues can be fixed.

Let’s start with the basics.

Checking The Facts: What Is A Good Mobile App Retention Rate?

A retention rate is the percentage of users that remain active on your mobile app after a certain period of time. It doesn’t necessarily pertain to how many people have uninstalled the app either. A sustained lack of activity is generally accepted as a sign that a user has lost interest in an app.

To calculate a good retention rate for your mobile app, be sure to take into account the frequency of logins you expect users to make. Some apps realistically should see daily logins, especially for gaming, dating, and social networking. Others, though, may only need weekly logins, like for ride-sharing apps, Google Authenticator or local business apps.

When calculating the retention rate for anticipated daily usage, you should run the calculation for at least a week, if not more. For weekly or monthly usage, adjust your calculation accordingly.

Recommended reading: Driving App Engagement With Personalization Techniques

Front-end is messy and complicated these days. That's why we publish articles, printed books and webinars with useful techniques to improve your work. Even better: Smashing Membership with a growing selection of front-end & UX goodies. So you get your work done, better and faster.

Explore Smashing Membership ↬

For daily usage, divide the following like so:

Users Logged into the App on Day 0 Users Logged into the App on Day 1 Users Logged into the App on Day 2 Users Logged into the App on Day 3 Users Logged into the App on Day 4 Users Logged into the App on Day 5 Users Logged into the App on Day 6 Users Logged into the App on Day 7

This will give you a curve that demonstrates how well your mobile app is able to sustain users. Here is an example of how you would calculate this:

Number of New Users Acquired Day 0 100 Day 1 91 (91% ) Day 2 85 (85%) Day 3 70 (70%) Day 4 60 (60%) Day 5 49 (49%) Day 6 32 (32%) Day 7 31 (31%)

If you can, add the data into a line graph format. It’ll be much easier to spot trends in downward momentum or plateauing:

An example of how to calculate and chart your app’s retention rate (Image source: Google Docs) (Large preview)

This is just a basic example of how a retention rate calculation works. Curious to see what the average (Android) mobile app’s retention curve looks like?

A Quettra study (with Andrew Chen) charted the following:

Average retention rates for Android apps (Image source: Andrew Chen) (Large preview)

According to this data, the average app loses 77% of users within just three days. By the time the first-month wraps, 90% of those original new users are gone.

Recent data shows that the average cost per installation of a mobile app (globally) breaks down to the following:

Average cost of each mobile app installation (Image source: Statista) (Large preview)

Basically, this is the average cost to build and market an app — a number you should aim to recuperate per user once the app has been installed. However, if your app loses about 90% of its users within a month’s time, think about what the loss actually translates to for your business.

Ankit Jain of Gradient Ventures summarized the key lesson to take away from these findings:

“Users try out a lot of apps but decide which ones they want to ‘stop using’ within the first 3-7 days. For ‘decent’ apps, the majority of users retained for 7 days stick around much longer. The key to success is to get the users hooked during that critical first 3-7 day period.”

As you can see from the charting of the top Android apps, Jain’s argument holds water:

Average retention rates for top Android apps (Image source: Andrew Chen) (Large preview)

Top Android apps still see a sharp decline in active users after about three days, but then the numbers plateau. They also don’t bleed as many new users upfront, which allows them to sustain a larger percentage of users.

This is exactly what you should be aiming for.

A Retention Recovery Guide For Mobile Apps

So, we know what makes for a good and bad retention rate. We also understand that it’s not about how many people have uninstalled or deleted the app from their devices. Letting an app sit in isolation, untouched on a mobile device, is just as bad.

As you can imagine, increasing your retention rate will lead to other big wins for your mobile app:

  • More engagement
  • More meaningful engagement
  • Greater loyalty
  • Increased conversions (if your app is monetized, that is)

Now you need to ask yourself:

“When are users dropping off? And why?”

You can draw your own hypotheses about this based on the retention rate alone, though it might be helpful to make use of tools like heat maps to spot problem areas in the mobile app. Once you know what’s going on, you can take action to remove the friction from the user experience.

To get you started, I’ve included a number of issues that commonly plague mobile apps with low retention rates. If your app is guilty of any of these, get to work on fixing the design or functionality ASAP!

1. Difficult Onboarding

Aside from the app store description and screenshots users encounter, onboarding is the first real experience they have with a mobile app. As you can imagine, a frustrating sign-in or onboarding procedure could easily turn off those who take that as a signal the rest of the app will be as difficult to use.

Let’s use the OkCupid dating app as an example. The initial splash screen looks great and is well-designed. It has a clear value proposition, and an easy-to-find call-to-action:

The first screen new OkCupid users encounter (Image source: OkCupid) (Large preview)

On the next page, users are given two options for joining the app. It’s free to use, but still requires users to create an account:

Account creation for OkCupid gives two options (Image source: OkCupid) (Large preview)

The first option is a Facebook sign-in. The other is to use a personal email address. Since Facebook logins can streamline not just signup, but the setup of dating mobile apps (since users can automatically import details, photos, and connections), this option is probably one many users’ choose.

But there’s a problem with it: After seven clicks to connect to Facebook and confirm one’s identity, here is what the user sees (or, at least, this is what I encountered the last couple of times I tried):

After connecting to Facebook, users still encounter an error signing in. (Image source: OkCupid) (Large preview)

One of the main reasons why users choose a Facebook sign-in is because of how quick and easy it’s supposed to be. In these attempts of mine, however, my OkCupid app wouldn’t connect to Facebook. So, after 14 total clicks (7 for each time I tried to sign up), I ended up having to provide an email anyway.

This is obviously not a great first impression OkCupid has left on me (or any of its users). What makes it worse is that we know there’s a lot more work to get onboarded with the app. Unlike competitors like Bumble that have greatly simplified signup, OkCupid forces users into a buggy onboarding experience as well as a more lengthy profile configuration.

Needless to say, this is probably a bit too much for some users.

2. Slow or Sloppy Navigation

Here’s another example of a time-waster for mobile app users.

Let’s say getting inside the new app is easy. There’s no real onboarding required. Maybe you just ask if it’s okay to use their location for personalization purposes or if you can send push notifications. Otherwise, users are able to start using the app right away.

That’s great — until they realize how clunky the experience is.

To start, navigation of a mobile app should be easy and ever-present. It’s not like a browser window where users can hit that “Back” button in order to get out of an unwanted page. On a mobile app, they need a clear and intuitive exit strategy. Also, navigation of an app should never take more than two or three steps to get to a desired outcome.

One example of this comes from Wendy’s. Specifically, I want to look at the “Offers” user journey:

The home page of the Wendy’s app promises “Offers” (Image source: Wendy's) (Large preview)

As you can see, the navigation at the bottom of the app is as clear as day. Users have three areas of the app they can explore — each of which makes sense for a business like Wendy’s. There are three additional navigation options in the top-right corner of the app, too.

When “Offers” is clicked, users are taken to a sort of full-screen pop-up containing all current special deals:

The Wendy’s Offers pop-up screen (Image source: Wendy's) (Large preview)

As you can see, the navigation is no longer there. The “X” for the Offers pop-up also sits in the top-left corner (instead of the right, which is the more intuitive choice). This is already a problem. It also persists throughout the entire Offers redemption experience.

Let’s say that users aren’t turned off by the poor navigational choices and still want to redeem one of these offers. This is what they encounter next:

Wendy’s Offers can be used at the restaurant. (Image source: Wendy's) (Large preview)

Now, this is pretty cool. Users can redeem the offer at that very moment while they’re in a Wendy’s or they can place the order through the app and pick it up. Either way, this is a great way to integrate the mobile app and in-store experiences.

Except…

The Wendy’s offer code takes a while to populate. (Image source: Wendy's) (Large preview)

Imagine standing in line at a Wendy’s or going through a drive-thru that isn’t particularly busy. That image above is not one you’d want to see.

They call it “fast food” for a reason and if your app isn’t working or it takes just a few seconds too long to load the offer code, imagine what that will do for everyone else’s experience at Wendy’s. The cashiers will be annoyed that they’ve held up the flow of traffic and everyone waiting in line will be frustrated in having to wait longer.

While mobile apps generally are designed to cater to the single user experience, you do have to consider how something like this could affect the experience of others.

Recommended reading: How To Improve Your Billing Form’s UX In One Day

3. Overwhelming Navigation

A poorly constructed or non-visible navigation is one thing. But a navigation that gives way too many options can be just as problematic. While a mega menu on something like an e-commerce website certainly makes sense, an oversized menu in mobile apps doesn’t.

It pains me to do this since I love the BBC, but its news app is guilty of this crime:

The top of the BBC News navigation (Image source: BBC News) (Large preview)

This looks like a standard news navigation at first glance. Top (popular) stories sit at the top; my News (customized) stories below it. But then it appears there’s more, so users are apt to scroll downwards and see what others options there are:

More of the BBC News navigation bar (Image source: BBC News) (Large preview)

The next scroll down gives users a choice of stories by geography, by subject:

Even more BBC News pages to choose from. (Image source: BBC News) (Large preview)

And then there are even more options for sports as well as specific BBC News channels. It’s a lot to take in.

If that weren’t bad enough, the personalization choices mirror the depth of the navigation:

BBC News personalization choices (Image source: BBC News) (Large preview)

Now, there’s nothing wrong with personalizing the mobile app experience. I think it’s something every app — especially those that deliver global news — should allow for. However, BBC News gives an overwhelming amount of options.

What’s worse is that many of the stories overlap categories, which means users could realistically see the same headlines over and over again as they scroll through the personalized categories they’ve chosen.

If BBC News (or any other app that does this) wants to allow for such deep personalization, the app should be programmed to hide stories that have already been seen or scrolled past — much like how Feedly handles its stream of news. That way, all that personalization really is valuable.

Recommended reading: How BBC Interactive Content Works Across AMP, Apps, And The Web

4. Outdated or Incomplete Experience

Anything a mobile app does that makes users unwillingly stop or slow down is bad. And this could be caused by a number of flaws in the experience:

  • Slow-loading pages,
  • Intrusive pop-ups,
  • Dated design choices,
  • Broken links or images,
  • Incomplete information,
  • And so on.

If you expect users to take time to download and at least give your app a try, make sure it’s worth their while.

One such example of this is the USHUD mobile app. It’s supposed to provide the same exact experience to users as the website counterpart. However, the app doesn’t work all that well:

A slow-loading page on the USHUD app (Image source: USHUD) (Large preview)

In the example above, you can see that search results are slow to load. Now, if they were chock full of images and videos, I could see why that might occur (though it’s still not really acceptable).

That said, many of the properties listed on the app don’t have corresponding visual content:

USHUD is missing images in search. (Image source: USHUD) (Large preview)

Real estate apps or, really, any apps that deal in the transaction of purchasing or renting of property or products should include images with each listing. It’s the whole reason why consumers are able to rent and buy online (or at least use it in the decisionmaking process).

But this app seems to be missing many images, which can lead to an unhelpful and unpleasant experience for users who hope to get information from the more convenient mobile app option.

If you’re going to build a mobile app that’s supposed to inform and compel users to engage, make sure it’s running in tip-top shape. All information is available. All tabs are accessible. And pages load in a reasonable timeframe.

5. Complicated or Impossible Gestures

We’ve already seen what a poorly made navigation can do to the user experience as well as the problem with pages that just don’t load. But sometimes friction can come from intentionally complicated gestures and engagements.

This is something I personally encountered with Sinemia recently. Sinemia is a competitor of the revolutionary yet failing MoviePass mobile app. Sinemia seems like a reasonable deal and one that could possibly sustain a lot longer than the unrealistic MoviePass model that promises entry to one movie every single day. However, Sinemia has had many issues with meeting the demand of its users.

To start, it delayed the sending of cards by a week. When I signed up in May, I was told I would have to wait at least 60 days to receive my card in the mail, even though my subscription had already kicked in. So, there was already a disparity there.

Sinemia’s response to that was to create a “Cardless” feature. This would enable those users who hadn’t yet received their cards to begin using their accounts. As you can see here, the FAQ included a dedicated section to Sinemia Cardless:

Questions about Sinemia Cardless (Image source: Sinemia) (Large preview)

See that point that says “I can confirm I have the latest Sinemia release installed…”? The reason why that point is there is because many Sinemia Cardless users (myself included) couldn’t actually activate the Cardless feature. When attempting to do so, the app would display an error.

The Sinemia FAQ then goes on to provide this answer to the complaint/question:

Sinemia Cardless issues with app version (Image source: Sinemia) (Large preview)

Here’s the problem: there were never any updates available for the mobile app. So, I and many others reached out to Sinemia for support. The answer repeatedly given was that Cardless could not work if your app ran on an old version. Support asked users to delete the app from their devices and reinstall from the app store to ensure they had the correct version — to no avail.

For me, this was a big problem. I was paying for a service that I had no way of using, and I was spending way too much time uninstalling and installing an app that should work straight out the gate.

I gave up after 48 hours of futile attempts. I went to my Profile to delete my account and get a refund on the subscription I had yet to use. But the app told me it was impossible to cancel my account through it. I tried to ask support for help, but no one responded. So, after Googling similar issues with account cancellations, I found that the only channel through which Sinemia would handle these requests was Facebook Messenger.

Needless to say, the whole experience left me quite jaded about apps that can’t do something as simple as activating or deactivating an account. While I recognize an urge to get a better solution on the mobile app market, rushing out an app and functionality that’s not ready to reach the public isn’t the solution.

Recommended reading: What You Need To Know About OAuth2 And Logging In With Facebook

6. Gated Content Keeps App from Being Valuable

For those of you who notice your retention rate remaining high for the first week or so from installation, the problem may more have to do with the mobile app’s limitations.

Recolor is a coloring book app I discovered in the app store. There’s nothing in the description that would lead me to believe that the app requires payment in order to enjoy the calming benefits of coloring pictures, but that’s indeed what I encountered:

Free drawings users can color with the Recolor app. (Image source: Recolor) (Large preview)

Above, you can see there are a number of free drawings available. Some of the more complex drawings will take some time to fill in, but not as much as a physical coloring book would by hand, which means users are apt to get through this quickly.

Inevitably, mobile app users will go searching for more options and this is what they will encounter:

Recolor’s more popular options are for Premium users. (Image source: Recolor) (Large preview)

When users look into some of the more popular drawings from Recolor, you’d hope they would encounter at least a few free drawings, right? After all, how many users could possibly be paying for a subscription to this app that’s not outrightly advertised as premium?

But it’s not just the Popular choices that require a fee to access (that’s what the yellow symbol in the bottom-right means). So, too, do most of the other categories:

Premium account needed to access more drawings on Recolor. (Image source: Recolor) (Large preview)

It’s a shame that so much of the content is gated off. Coloring books have proven to be good for managing anxiety and stress, so leaving users with only a few dozen options doesn’t seem right. Plus, the weekly membership to the app is pretty expensive, even if users try to earn coins by watching videos.

A mobile app such as this one should make its intentions clear from the start: “Consider this a free trial. If you want more, you’ll have to pay.”

While I’m sure the developer didn’t intend to deceive with this app model, I can see how the retention rate might suffer and prevent this app from becoming a long-term staple on many users’ devices.

When making a promise to users (even if it’s implied), design and manage your app in a way that lives up to those expectations.

As I noted earlier, those initial signups might make you hopeful of the app’s long-term potential, but a forced pay-to-play scenario could easily disrupt that after just a few weeks.

7. Impossible to Convert in-App

Why do we create mobile apps? For many developers, it’s because the mobile web experience is insufficient. And because many users want a more convenient way to connect with your brand. A mobile app sits on the home screen of devices and requires just a single click to get inside.

So, why would someone build an app that forces users to leave it in order to convert? It seems pointless to even go through the trouble of creating the app in the first place (which is usually no easy task).

Here’s the Megabus app:

The Megabus mobile app for searching and buying tickets (Image source: Megabus) (Large preview)

Megabus is a low-cost transportation service that operates in Canada, the United States and the United Kingdom. There are a number of reasons why users would gravitate to the mobile app counterpart for the website; namely, the convenience of logging in and purchasing tickets while they’re traveling.

The image above shows the search I did for Megabus tickets through the mobile app. I entered all pertinent details, found tickets available for my destination and got ready to “Buy Tickets” right then and there.

However, you can’t actually buy tickets from the mobile app:

Megabus tickets are only available online. (Image source: Megabus) (Large preview)

Upon clicking “Buy Tickets”, the app pushes users out and into their browser. They then are asked to reinput all those details from the mobile app to search for open trips and make a purchase.

For a service that’s supposed to make travel over long distances convenient, its mobile app has done anything but reinforce that experience.

For those of you considering building an app (whether on your own accord or because a client asked) solely so you can land a spot in app store search results, don’t waste users’ time. If they can’t have a complete experience within the app, you’re likely to see your retention rate tank fairly quickly.

Wrapping Up

Clearly, there are a number of ways in which a mobile app may suffer a misstep in terms of the user experience. And I’m sure that there are times when mobile app developers don’t even realize there’s something off in the experience.

This is why your mobile app retention rate is such a critical data point to pay attention to. It’s not enough to just know what that rate is. You should watch for when those major dropoffs occur; not just in terms of the timeline, but also in terms of which pages lead to a stoppage in activity or an uninstall altogether.

With this data in hand, you can refine the in-app experience and make it one that users want to stay inside of for the long run.

(ra, yk, il)
Categories: Around The Web

Live Accessibility And Performance Audits At SmashingConf Toronto

Smashing Magazine - Wed, 10/17/2018 - 6:20am
Live Accessibility And Performance Audits At SmashingConf Toronto Live Accessibility And Performance Audits At SmashingConf Toronto Markus Seyfferth 2018-10-17T13:20:35+02:00 2018-11-08T09:22:50+00:00

Earlier this year, many of your favorite speakers were featured at SmashingConf Toronto, however, things were quite different this time. The speakers had been asked to present without slides. It was interesting to see the different ways our speakers approached the challenge.

Two of our speakers chose to demonstrate how they audit a site or application live on stage: Marcy Sutton on accessibility, and Tim Kadlec on performance. Watch the videos to see an expert perform these audits, and see if there is anything you can take back to your own testing processes.

To watch all of the videos recorded in Toronto, head on over to our SmashingConf Vimeo channel.

Accessibility: Marcy Sutton

Marcy took two example components, built using React, and walked us through how these components could be made more accessible with some straightforward changes.

Performance: Tim Kadlec

Tim demonstrates how to test the performance of a site, and find bottlenecks leading to poor experiences for visitors. If you have ever wondered how to get started testing for performance, this is a talk you will find incredibly useful.

Enjoyed watching these talks? There are many more videos from SmashingConf Toronto on Vimeo. We’re also getting ready for SmashingConf New York next week — see you there? ;-)

(ra, il)
Categories: Around The Web
Syndicate content