Thursday, 30 January 2020

How To Create A Card Matching Game Using Angular And RxJS

How To Create A Card Matching Game Using Angular And RxJS

Anna Prenzel

Today, I’d like to focus on data streams resulting from click events on the user interface. The processing of such clickstreams is particularly useful for applications with an intensive user interaction where many events have to be processed. I’d also like to introduce you to RxJS a bit more; it’s a JavaScript library that can be used to express event handling routines compactly and concisely in a reactive style.

What Are We Building?

Learning games and knowledge quizzes are popular both for younger and older users. An example is the game “pair matching”, where the user has to find related pairs in a mixture of images and/or text snippets.

The animation below shows a simple version of the game: The user selects two elements on the left and right side of the playing field one after the other, and in any order. Correctly matched pairs are moved to a separate area of the playing field, while any wrong assignments are immediately dissolved so that the user has to make a new selection.

Screen capture of the learningl game “matching pairs”
A sneak peek of the game we’ll be creating today

In this tutorial, we will build such a learning game step by step. In the first part, we will build an Angular component that is just showing the playing field of the game. Our aim is that the component can be configured for different use cases and target groups — from an animal quiz up to a vocabulary trainer in a language learning app. For this purpose, Angular offers the concept of content projection with customizable templates, which we will make use of. To illustrate the principle, I will build two versions of the game (“game1” and “game2”) with different layouts.

In the second part of the tutorial, we will focus on reactive programming. Whenever a pair is matched, the user needs to get some sort of feedback from the app; it is this event handling that is realized with the help of the library RxJS.

1. Building An Angular Component For The Learning Game

How To Create The Basic Framework

First, let’s create a new project named “learning-app”. With the Angular CLI, you can do this with the command ng new learning-app. In the file app.component.html, I replace the pre-generated source code as follows:

<div style="text-align:center"> 
  <h1>Learning is fun!</h1>
</div>

In the next step, the component for the learning game is created. I’ve named it “matching-game” and used the command ng generate component matching-game. This will create a separate subfolder for the game component with the required HTML, CSS and Typescript files.

As already mentioned, the educational game must be configurable for different purposes. To demonstrate this, I create two additional components (game1 and game2) by using the same command. I add the game component as a child component by replacing the pre-generated code in the file game1.component.html or game2.component.html with the following tag:

<app-matching-game></app-matching-game>

At first, I only use the component game1. In order to make sure that game 1 is displayed immediately after starting the application, I add this tag to the app.component.html file:

<app-game1></app-game1>

When starting the application with ng serve --open, the browser will display the message “matching-game works”. (This is currently the only content of matching-game.component.html.)

Now, we need to test the data. In the /app folder, I create a file named pair.ts where I define the class Pair:

export class Pair {
  leftpart: string;
  rightpart: string;
  id: number;
}

A pair object comprises two related texts (leftpart and rightpart) and an ID.

The first game is supposed to be a species quiz in which species (e.g. dog) have to be assigned to the appropriate animal class (i.e. mammal).

In the file animals.ts, I define an array with test data:

import { Pair } from './pair';
export const ANIMALS: Pair[] = [
  { id: 1, leftpart: 'dog', rightpart: 'mammal'},
  { id: 2, leftpart: 'blickbird', rightpart: 'bird'},
  { id: 3, leftpart: 'spider', rightpart: 'insect'},
  { id: 4, leftpart: 'turtle', rightpart: 'reptile' },
  { id: 5, leftpart: 'guppy', rightpart: 'fish'},
];

The component game1 needs access to our test data. They are stored in the property animals. The file game1.component.ts now has the following content:

import { Component, OnInit } from '@angular/core';
import { ANIMALS } from '../animals';
@Component({
  selector: 'app-game1',
  templateUrl: './game1.component.html',
  styleUrls: ['./game1.component.css']
})
export class Game1Component implements OnInit {
  animals = ANIMALS;
  constructor() { }
  ngOnInit() {
  }
}

The First Version Of The Game Component

Our next goal: The game component matching-game has to accept the game data from the parent component (e.g. game1) as input. The input is an array of “pair” objects. The user interface of the game should be initialized with the passed objects when starting the application.

Screen capture of the learningl game “matching pairs”

For this purpose, we need to proceed as follows:

  1. Add the property pairs to the game component using the @Input decorator.
  2. Add the arrays solvedPairs and unsolvedPairs as additional private properties of the component. (It is necessary to distinguish between already “solved” and “not yet solved” pairs.)
  3. When the application is started (see function ngOnInit) all pairs are still “unsolved” and are therefore moved to the array unsolvedPairs.
import { Component, OnInit, Input } from '@angular/core';
import { Pair } from '../pair';
@Component({
  selector: 'app-matching-game',
  templateUrl: './matching-game.component.html',
  styleUrls: ['./matching-game.component.css']
})

export class MatchingGameComponent implements OnInit {
  @Input() pairs: Pair[];
  private solvedPairs: Pair[] = [];
  private unsolvedPairs: Pair[] = [];
  constructor() { }
  ngOnInit() {      
    for(let i=0; i<this.pairs.length; i++){    
        this.unsolvedPairs.push(this.pairs[i]);
    }
  }
}

Furthermore, I define the HTML template of the matching-game component. There are containers for the unsolved and solved pairs. The ngIf directive ensures that the respective container is only displayed if at least one unsolved or solved pair exists.

In the container for the unsolved pairs (class container unsolved), first all left (see the left frame in the GIF above) and then all right (see the right frame in the GIF) components of the pairs are listed. (I use the ngFor directive to list the pairs.) At the moment, a simple button is sufficient as a template.

With the template expression and {}, the values of the properties leftpart and rightpart of the individual pair objects are queried when iterating the pair array. They are used as labels for the generated buttons.

The assigned pairs are listed in the second container (class container solved). A green bar (class connector) indicates that they belong together.

The corresponding CSS code of the file matching-game.component.css can be found in the source code at the beginning of the article.

<div id="game">
   <div class="container unsolved" *ngIf="unsolvedPairs.length>0">
      <div class="pair_items left">
         <button *ngFor="let pair of unsolvedPairs" class="item">  
             
         </button>        
      </div>
    <div class="pair_items right">
      <button *ngFor="let pair of unsolvedPairs" class="item"> 
            
         </button>  
    </div>
   </div>
   <div class="container solved" *ngIf="solvedPairs.length>0">
       <div *ngFor="let pair of solvedPairs" class="pair">
          <button></button>
          <div class="connector"></div>
          <button></button>
       </div>
   </div>
</div>

In the component game1, the array animals is now bound to the pairs property of the component matching-game (one-way data binding).

<app-matching-game [pairs]="animals"></app-matching-game>

The result is shown in the image below.

Current state of the user interface
Current state of the user interface

Obviously, our matching game is not too difficult yet, because the left and right parts of the pairs are directly opposite each other. So that the pairing is not too trivial, the right parts should be mixed. I solve the problem with a self-defined pipe shuffle, which I apply to the array unsolvedPairs on the right side (the parameter test is needed later to force the pipe to be updated):

...
<div class="pair_items right">
  <button *ngFor="let pair of unsolvedPairs | shuffle:test" class="item"> 
        
  </button>  
</div>
...

The source code of the pipe is stored in the file shuffle.pipe.ts in the app folder (see source code at the beginning of the article). Also note the file app.module.ts, where the pipe must be imported and listed in the module declarations. Now the desired view appears in the browser.

Extended Version: Using Customizable Templates To Allow An Individual Design Of The Game

Instead of a button, it should be possible to specify arbitrary template snippets to customize the game. In the file matching-game.component.html I replace the button template for the left and right side of the game with an ng-template tag. I then assign the name of a template reference to the property ngTemplateOutlet. This gives me two placeholders, which are replaced by the content of the respective template reference when rendering the view.

We are here dealing with the concept of content projection: certain parts of the component template are given from outside and are “projected” into the template at the marked positions.

When generating the view, Angular must insert the game data into the template. With the parameter ngTemplateOutletContext I tell Angular that a variable contextPair is used within the template, which should be assigned the current value of the pair variable from the ngFor directive.

The following listing shows the replacement for the container unsolved. In the container solved, the buttons have to be replaced by the ng-template tags as well.

<div class="container unsolved" *ngIf="unsolvedPairs.length>0">
<div class="pair_items left">        
    <div *ngFor="let pair of unsolvedPairs" class="item">
         <ng-template [ngTemplateOutlet]="leftpart_temp" 
             [ngTemplateOutletContext]="{contextPair: pair}">
       </ng-template>
    </div>    
</div>    
<div class="pair_items right">
    <div *ngFor="let pair of unsolvedPairs | shuffle:test" class="item">           
         <ng-template [ngTemplateOutlet]="leftpart_temp"
           [ngTemplateOutletContext]="{contextPair: pair}">
       </ng-template>
    </div>
</div>
</div>
...

In the file matching-game.component.ts, the variables of both template references (leftpart_temp and rightpart_temp) must be declared. The decorator @ContentChild indicates that this is a content projection, i.e. Angular now expects that the two template snippets with the respective selector (leftpart or rightpart) are provided in the parent component between the tags <app-matching-game></app-matching-game> of the host element (see @ViewChild).

@ContentChild('leftpart', {static: false}) leftpart_temp: TemplateRef<any>;
@ContentChild('rightpart', {static: false}) rightpart_temp: TemplateRef<any>;

Don’t forget: The types ContentChild and TemplateRef must be imported from the core package.

In the parent component game1, the two required template snippets with the selectors leftpart and rightpart are now inserted.

For the sake of simplicity, I will reuse the buttons here again:

<app-matching-game [pairs]="animals">
    <ng-template #leftpart let-animalPair="contextPair">
          <button></button>       
       </ng-template>
    <ng-template #rightpart let-animalPair="contextPair">
          <button></button>
       </ng-template>
</app-matching-game>

The attribute let-animalPair="contextPair" is used to specify that the context variable contextPair is used in the template snippet with the name animalPair.

The template snippets can now be changed to your own taste. To demonstrate this I use the component game2. The file game2.component.ts gets the same content as game1.component.ts. In game2.component.html I use an individually designed div element instead of a button. The CSS classes are stored in the file game2.component.css.

<app-matching-game [pairs]="animals">
    <ng-template #leftpart let-animalPair="contextPair">
          <div class="myAnimal left"></div>        
       </ng-template>
    <ng-template #rightpart let-animalPair="contextPair">
          <div class="myAnimal right"></div>
       </ng-template>
</app-matching-game>

After adding the tags <app-game2></app-game2> on the homepage app.component.html, the second version of the game appears when I start the application:

An alternative view of the game in the component game2
An alternative view of the game in the component game2

The design possibilities are now almost unlimited. It would be possible, for example, to define a subclass of Pair that contains additional properties. For example, image addresses could be stored for the left and/or right parts. The images could be displayed in the template along with the text or instead of the text.

2. Control Of User Interaction With RxJS

Advantages Of Reactive Programming With RxJS

To turn the application into an interactive game, the events (e.g. mouse click events) that are triggered at the user interface must be processed. In reactive programming, continuous sequences of events, so-called “streams”, are considered. A stream can be observed (it is an “observable”), i.e. there can be one or more “observers” or “subscribers” subscribing to the stream. They are notified (usually asynchronously) about every new value in the stream and can react to it in a certain way.

With this approach, a low level of coupling between the parts of an application can be achieved. The existing observers and observables are independent of each other and their coupling can be varied at runtime.

The JavaScript library RxJS provides a mature implementation of the Observer design pattern. Furthermore, RxJS contains numerous operators to convert streams (e.g. filter, map) or to combine them into new streams (e.g. merge, concat). The operators are “pure functions” in the sense of functional programming: They do not produce side effects and are independent of the state outside the function. A program logic composed only of calls to pure functions does not need global or local auxiliary variables to store intermediate states. This, in turn, promotes the creation of stateless and loosely coupled code blocks. It is therefore desirable to realize a large part of the event handling by a clever combination of stream operators. Examples of this are given in the section after next, based on our matching game.

Integrating RxJS Into The Event Handling Of An Angular Component

The Angular framework works with the classes of the RxJS library. RxJS is therefore automatically installed when Angular is installed.

The image below shows the main classes and functions that play a role in our considerations:

A model of the essential classes for event handling in Angular/RxJS
A model of the essential classes for event handling in Angular/RxJS
Class Name Function
Observable (RxJS) Base class that represents a stream; in other words, a continuous sequence of data. An observable can be subscribed to. The pipe function is used to apply one or more operator functions to the observable instance.
Subject (RxJS) The subclass of observable provides the next function to publish new data in the stream.
EventEmitter (Angular) This is an angular-specific subclass that is usually only used in conjunction with the @Output decorator to define a component output. Like the next function, the emit function is used to send data to the subscribers.
Subscription (RxJS) The subscribe function of an observable returns a subscription instance. It is required to cancel the subscription after using the component.

With the help of these classes, we want to implement the user interaction in our game. The first step is to make sure that an element that is selected by the user on the left or right side is visually highlighted.

The visual representation of the elements is controlled by the two template snippets in the parent component. The decision how they are displayed in the selected state should therefore also be left to the parent component. It should receive appropriate signals as soon as a selection is made on the left or right side or as soon as a selection is to be undone.

For this purpose, I define four output values of type EventEmitter in the matching-game.component.ts file. The types Output and EventEmitter have to be imported from the core package.

@Output() leftpartSelected = new EventEmitter<number>();
@Output() rightpartSelected = new EventEmitter<number>();
@Output() leftpartUnselected = new EventEmitter();
@Output() rightpartUnselected = new EventEmitter();

In the template matching-game.component.html, I react to the mousedown event on the left and right side, and then send the ID of the selected item to all receivers.

<div *ngFor="let pair of unsolvedPairs" class="item" (mousedown)="leftpartSelected.emit(pair.id)">
...
<div *ngFor="let pair of unsolvedPairs | shuffle:test" class="item" (mousedown)="rightpartSelected.emit(pair.id)">

In our case, the receivers are the components game1 and game2. There you can now define the event handling for the events leftpartSelected, rightpartSelected, leftpartUnselected and rightpartUnselected. The variable $event represents the emitted output value, in our case the ID. In the following you can see the listing for game1.component.html, for game2.component.html the same changes apply.

<app-matching-game [pairs]="animals" (leftpartSelected)="onLeftpartSelected($event)" (rightpartSelected)="onRightpartSelected($event)" (leftpartUnselected)="onLeftpartUnselected()" (rightpartUnselected)="onRightpartUnselected()">

      <ng-template #leftpart let-animalPair="contextPair">
           <button [class.selected]="leftpartSelectedId==animalPair.id"> 
           
           </button>       
      </ng-template>    
    <ng-template #rightpart let-animalPair="contextPair">
        <button [class.selected]="rightpartSelectedId==animalPair.id"> 
        
        </button> 
     </ng-template>
</app-matching-game>

In game1.component.ts (and similarly in game2.component.ts), the event handler functions are now implemented. I store the IDs of the selected elements. In the HTML template (see above), these elements are assigned the class selected. The CSS file game1.component.css defines which visual changes this class will bring about (e.g. color or font changes). Resetting the selection (unselect) is based on the assumption that the pair objects always have positive IDs.

onLeftpartSelected(id:number):void{
    this.leftpartSelectedId = id;
}
onRightpartSelected(id:number):void{
    this.rightpartSelectedId = id;
}
onLeftpartUnselected():void{
    this.leftpartSelectedId = -1;
}
onRightpartUnselected():void{
    this.rightpartSelectedId = -1;
}

In the next step, event handling is required in the matching game component. It must be determined if an assignment is correct, that is, if the left selected element matches the right selected element. In this case, the assigned pair can be moved into the container for the resolved pairs.

I would like to formulate the evaluation logic using RxJS operators (see the next section). For preparation, I create a subject assignmentStream in matching-game.component.ts. It should emit the elements selected by the user on the left or right side. The goal is to use RxJS operators to modify and split the stream in such a way that I get two new streams: one stream solvedStream which provides the correctly assigned pairs and a second stream failedStream which provides the wrong assignments. I would like to subscribe to these two streams with subscribe in order to be able to perform appropriate event handling in each case.

I also need a reference to the created subscription objects, so that I can cancel the subscriptions with “unsubscribe” when leaving the game (see ngOnDestroy). The classes Subject and Subscription must be imported from the package “rxjs”.

private assignmentStream = new Subject();

private solvedStream = new Observable<Pair>();
private failedStream = new Observable<string>();

private s_Subscription: Subscription;
private f_Subscription: Subscription;

ngOnInit(){

  ...
  //TODO: apply stream-operators on
  //leftpartClicked und rightpartClicked
  this.s_Subscription = this.solvedStream.subscribe(pair =>   
  handleSolvedAssignment(pair));
  this.f_Subscription = this.failedStream.subscribe(() =>    
  handleFailedAssignment());
}

ngOnDestroy() {
   this.s_Subscription.unsubscribe();
   this.f_Subscription.unsubscribe();
}

If the assignment is correct, the following steps are done:

  • The assigned pair is moved to the container for the solved pairs.
  • The events leftpartUnselected and rightpartUnselected are sent to the parent component.

No pair is moved if the assignment is incorrect. If the wrong assignment was executed from left to right (side1 has the value left), the selection should be undone for the element on the left side (see the GIF at the beginning of the article). If an assignment is made from right to left, the selection is undone for the element on the right side. This means that the last element that was clicked on remains in a selected state.

For both cases, I prepare the corresponding handler functions handleSolvedAssignment and handleFailedAssignment (remove function: see source code at the end of this article):

private handleSolvedAssignment(pair: Pair):void{
   this.solvedPairs.push(pair);
   this.remove(this.unsolvedPairs, pair);    
   this.leftpartUnselected.emit();
   this.rightpartUnselected.emit();
   //workaround to force update of the shuffle pipe
   this.test = Math.random() * 10;
}
private handleFailedAssignment(side1: string):void{

   if(side1=="left"){        
        this.leftpartUnselected.emit();        
   }else{            
        this.rightpartUnselected.emit();
   }  

}

Now we have to change the viewpoint from the consumer who subscribes to the data to the producer who generates the data. In the file matching-game.component.html, I make sure that when clicking on an element, the associated pair object is pushed into the stream assignmentStream. It makes sense to use a common stream for the left and right side because the order of the assignment is not important for us.

<div *ngFor="let pair of unsolvedPairs" class="item" (mousedown)="leftpartSelected.emit(pair.id)"
(click)="assignmentStream.next({pair: pair, side: 'left'})">
...
<div *ngFor="let pair of unsolvedPairs | shuffle:test" class="item" (mousedown)="rightpartSelected.emit(pair.id)" 
(click)="assignmentStream.next({pair: pair, side: 'right'})">

Design Of The Game Interaction With RxJS Operators

All that remains is to convert the stream assignmentStream into the streams solvedStream and failedStream. I apply the following operators in sequence:

pairwise

There are always two pairs in an assignment. The pairwise operator picks the data in pairs from the stream. The current value and the previous value are combined into a pair.

From the following stream…

„{pair1, left},  {pair3, right},  {pair2, left},  {pair2, right},  {pair1, left},  {pair1, right}“

…results this new stream:

„({pair1, left}, {pair3, right}),   ({pair3, right}, {pair2, left}),   ({pair2, left}, {pair2, right}),   ({pair2, right}, {pair1, left}),   ({pair1, left}, {pair1, right})“
 

For example, we get the combination ({pair1, left}, {pair3, right}) when the user selects dog (id=1) on the left side and insect (id=3) on the right side (see array ANIMALS at the beginning of the article). These and the other combinations result from the game sequence shown in the GIF above.

filter

You have to remove all combinations from the stream that were made on the same side of the playing field like ({pair1, left}, {pair1, left}) or ({pair1, left}, {pair4, left}).

The filter condition for a combination comb is therefore comb[0].side != comb[1].side.

partition

This operator takes a stream and a condition and creates two streams from this. The first stream contains the data that meets the condition and the second stream contains the remaining data. In our case, the streams should contain correct or incorrect assignments. So the condition for a combination comb is comb[0].pair===comb[1].pair.

The example results in a „correct” stream with

({pair2, left}, {pair2, right}),   ({pair1, left}, {pair1, right})
 

and a “wrong” stream with

({pair1, left}, {pair3, right}), ({pair3, right}, {pair2, left}),  ({pair2, right}, {pair1, left})
 

map

Only the individual pair object is required for further processing of a correct assignment, such as pair2. The map operator can be used to express that the combination comb should be mapped to comb[0].pair. If the assignment is incorrect, the combination comb is mapped to the string comb[0].side because the selection should be reset on the side specified by side.

The pipe function is used to concatenate the above operators. The operators pairwise, filter, partition, map must be imported from the package rxjs/operators.

ngOnInit() {    
   ...  
   const stream = this.assignmentStream.pipe(
                   pairwise(),
                   filter(comb => comb[0].side != comb[1].side)                    
                  );
   //pipe notation leads to an error message (Angular 8.2.2, RxJS 6.4.0)      
   const [stream1, stream2] = partition(comb => 
                                        comb[0].pair === comb[1].pair)(stream);
   this.solvedStream = stream1.pipe( 
                         map(comb => comb[0].pair)
                       );
   this.failedStream = stream2.pipe(
                         map(comb => comb[0].side)
                       );
   this.s_Subscription = this.solvedStream.subscribe(pair => 
                             this.handleSolvedAssignment(pair));
   this.f_Subscription = this.failedStream.subscribe(side => 
                             this.handleFailedAssignment(side));
}

Now the game already works!

Screen capture of the learningl game “matching pairs”
Final result

By using the operators, the game logic could be described declaratively. We only described the properties of our two target streams (combined into pairs, filtered, partitioned, remapped) and did not have to worry about the implementation of these operations. If we had implemented them ourselves, we would also have had to store intermediate states in the component (e.g. references to the last clicked items on the left and right side). Instead, the RxJS operators encapsulate the implementation logic and the required states for us and thus raise the programming to a higher level of abstraction.

Conclusion

Using a simple learning game as an example, we tested the use of RxJS in an Angular component. The reactive approach is well suited to process events that occur on the user interface. With RxJS, the data needed for event handling can be conveniently arranged as streams. Numerous operators, such as filter, map or partition are available for transforming the streams. The resulting streams contain data that is prepared in its final form and can be subscribed to directly. It requires a little skill and experience to select the appropriate operators for the respective case and to link them efficiently. This article should provide an introduction to this.

Further Resources

Related Reading on SmashingMag:

Smashing Editorial
(ra, il)

from Tumblr https://ift.tt/36EfMa3

Tuesday, 28 January 2020

How Smashing Magazine Manages Content: Migration From WordPress To JAMStack

How Smashing Magazine Manages Content: Migration From WordPress To JAMStack

Sarah Drasner

(This is a sponsored article.) Every time developers talk about WordPress, their market share percentage changes. “20% of all sites are on WordPress!” “40% of all sites are on WordPress!” Whatever the percentage is, the message is the same: in terms of adoption, WordPress is MASSIVE.

So why, with that kind of adoption, would a site that’s using WordPress consider moving to JAMstack? In this two-part article series, we’ll cover what an actual WordPress migration looks like, using a case study of the very site you’re reading from right now.

We’ll talk through the gains and losses, the things we wish we knew earlier, and what we were surprised by. And then we’ll follow it up with a technical demonstration of one possible migration path, not off WordPress completely, but how you can serve decoupled WordPress so that you can have the best of both worlds: a JAMstack implementation of WordPress that gives you all the power of their dashboard and functionality, with better performance and security.

Let’s dig in!

NetlifyThis article has been kindly supported by our dear friends at Netlify, an all-in-one platform for automating modern web projects. Thank you!

Why?

In 2015, Netlify co-founder Mathias Biilmann and Smashing founder Vitaly Friedman talked shop. As JAMStack architecture started making rounds, Smashing has got more interested in the idea of the stack. Vitaly and Markus (former managing director at Smashing) asked Matt what would happen if Smashing migrated from their traditional WordPress/LAMPstack site to a JAMstack architecture.

As an experiment, Matt scraped all the HTML from Smashing and hosted it on Netlify, delivering the content statically from a CDN. The results were compelling — the static version is more than six times as fast on average!

This type of architecture works so well in part because pages aren’t being compiled on-demand as you visit them. Since you’re serving pre-built content directly from a Content Delivery Network, the site is already “there” and ready for the user.

Since you’re serving via CDN, you can also distribute the content across the globe — closer to potential visitors. There’s no central point of origin, which is vital in the case of any online publication that wants to be fast for all of its readers.

So the stage was set. Smashing Magazine migrated to the JAMstack — to Netlify as a platform in particular. In its 10 years of operation, Smashing had grown from a small online publication to a massive WordPress blog, selling things like books, conference tickets, and workshops.

There were a few pieces of this site:

  • WordPress blog
  • Rails jobs board
  • Shopify store
  • Another CMS for the conference site

When Netlify first began the migration, the site was suffering some performance issues, weighed down by 20K comments and thousands of articles. Smashing was also interested in authentication as part of a new subscription plan, as well as a redesign for a more modern look.

Reliability

Smashing regularly achieves what other platforms dream of: articles shared widely through an enormous community. However, when a tipping point of virility was reached for a post, Smashing regularly had issues with outages. To mitigate this, the heavy use of WordPress plugins were introduced into their stack, but they still struggled to retain good uptime metrics.

Moving to Netlify allowed the Smashing team to avoid getting database connection errors and not worry about downtime even when an article saw a huge amount of traffic. Why? Because when serving without a server, the prebuilt content doesn’t have to be generated and served — it already exists, ready to be viewed. Nothing is being requested on the spot except for the entire, static page.

Serving via CDN also allowed Smashing to sleep a little easier in terms of security. wp-login.php has long been a source of security holes and attack vectors. Prebuilt content can not be accessed in the same way and security holes are not as ubiquitous.

Cache Invalidation

Smashing had cycled through every WordPress caching plugin, with varied results and a lot of problems. Vitaly Friedman of Smashing mentions,

“The main issues we had were related to ‘Error Establishing Database Connection’ that we kept having every other week, and we literally tried every single WordPress caching plugin out there. The performance was pretty OK (overall), but we were looking to improve it further. Plus, we did want to launch Membership and connect all the different offerings — conferences, job posts, articles, books, eBooks — with one single platform, and it was remarkably difficult to achieve with WordPress in play.”

Moving to Netlify allowed the Smashing team to see instant cache invalidation while also serving cached and performant content, with no extra overhead.

When you deploy a site, HTML files are hosted on Netlify’s CDN. It’s optimized for a high cache-hit rate, and fast time to first byte, while being able to instantly invalidate all HTML files that have changed. Netlify also fingerprints all links to assets like CSS files, images, fonts, or JS files, and serves Smashing with caching headers that cache them forever. The fingerprinting guarantees they’re unique, and that if you update a new version, the newer version is served instead.

Workflow

Looking at the existing setup, one large reason for the migration was simply to unify the existing properties. Having to context shift between all of these different tech stacks and setups became a hard maintenance problem that tasked the engineers.

When previously their infrastructure was split up among so many different systems, this migration process also unified everything, keeping the main site, the conference site, the subscriptions and e-commerce section all working together instead of maintained separately with different stacks. This helped keep development costs low and developer experience working on all properties consistently.

The WordPress migration piece proved to be the largest and most delicate. Netlify tried to export the data from the WP exporter, only to find that the content had embeds that needed to be preserved, or at times were altered by plugins. In order to maintain parity with what was on the site, a series of scrapers were written, broken down by articles, assets, comments, and the homepage.

Once that was written and transformed, it was loaded into a new repo in GitHub, and Netlify CMS was used instead. What makes Netlify CMS unique is that it’s lightweight, and integrates content editors into a Git workflow — meaning it will literally pull and serve markdown files from a git repo instead of a database. In addition, Netlify CMS is platform agnostic and works with (almost) all static site generators and sites stored in GitHub.

At that time, Sara Soueidan worked for Smashing as a freelance front-end developer on their redesign. She created a library of components to build out the front-end architecture and remarked how much more simple it was to work with because she was working directly in git, even when working with the CMS.

“Everything that I pushed to the repository is being directly applied to the pattern library which means that you don’t have to maintain two different sets of components… this type of continuity was great! All I have to do is write HTML, CSS, and JavaScript and push to the repo and everything works like magic. The workflow was fantastic.”

All of this said, Netlify CMS can sometimes be too lightweight for such a high traffic and scale use case. Smashing regularly has guest authors and a full editorial staff. Some of the rich features WordPress offers are really helpful for these kinds of highly collaborative environments.

That’s why in the following tutorial, we’ll walk you through a headless model, where you can still reap the benefits of the WordPress dashboard for content creators, but use WordPress via API and have the development rely on a git-centric workflow that easy for developers to maintain as well. Stay tuned!

Framework Choices

In the initial prototype that Matt Biilmann created, he wrote everything in minimal Preact, paired with Hugo, as he was very focused on performance. He just used props and kept everything very lightweight. As he passed the project off to be maintained by Smashing’s developer, Ilya Pukhalski, he found that Preact was lacking some features they were missing to tap into React’s ecosystem. Eventually, the benefits of Redux and other libraries outweighed the cost.

Reflecting now, Matt says he would have used Vue, which didn’t have quite the market share at the time (I swear I didn’t prompt him to say that). I asked the obvious question: why not Svelte? As performance-minded folks tend to reach for that library. He mentioned that Svelte doesn’t quite have the ecosystem Vue has yet.

When Matt reflects on whether or not he would have still used Hugo, he says that he loves Hugo, but what he found difficult for this project in particular was that it didn’t have enough of a plugin system — creating banner ads and things of that nature were not programmable enough with Hugo and he needed to inject scripts to accomplish it. These scripts tended to slow the build process down. That said, we still use Hugo for our own site at netlify.com and love it — this caveat is extremely particular to the needs of Smashing’s site in particular.

If he could do it again, he might choose either Eleventy, which has rich capabilities in terms of creating plugins and other extendable scripts. Or, if he was using Vue, Nuxt would have offered him some nice plugin capabilities while allowing being a good choice for that framework, offering server-side rendering, routing, and static generation.

Building Large Sites

There was one problem that emerged working with a site as large as Smashing and maybe you can already figure out what it is, we just touched on it. It’s true that with any large site of prebuilt pages served on a CDN, the performance is still great because we’re not building anything on the fly for the users.

But that benefit can only happen if the site is pre-built, and that process can be time-consuming. While more traditional sites will build the pages when you request them, we’re literally creating every single page in advance just in case the user might need it. It makes the performance super fast! But that time is now offloaded to development and publishing time — creating every page can take time.

This is not so much of an issue with smaller sites, but at Smashing Magazine’s scale, we need to think about that time a bit more, especially so that people can keep productivity high while actively daily creating content.

What Netlify did was create a large /production-articles folder which carried the bulk of the many 1000s of articles they were already hosting. Then, made a separate working directory called content/articles where the articles that were actively being created and edited could be placed.

This build process meant that everyone who was working on the site was working with a smaller batch of articles for local development, unhindered by waiting for the entire build. This process was managed by a gulp task to prepare the production articles, and freed Hugo up to only handle what was actively being worked on.

One of the cons of this approach is that it does still require the entire build to be run, making the process slow. At a smaller publication, this would likely matter less but at Smashing’s scale, it does slow down the publication process.

Open-Source APIs

In the beginning, we mentioned that among other things, Smashing was interested in migrating their existing e-commerce solution, handle comments outside of WordPress, and add functionality for auth. All of these pieces of functionality were built with open-source solutions that Netlify maintains, breaking them out into stateless APIs:

  • GoTell
    An API and build tool for handling large amounts of comments.
  • GoCommerce
    A small Go-based API for e-commerce sites that handles orders and payments.
  • GoTrue
    A small open-source API written in Golang that can act as a self-standing API service for handling user registration and authentication for projects. It’s based on OAuth2 and JWT and will handle user signup, authentication, and custom user data.

Each one of these pieces required migration and unique factors of their own, and while they’re out of scope for this article, they’re all covered in a free book Matt co-wrote called “Modern Web Development on the JAMstack”. We’ll also do some deep dives like this one — with usable examples — into search, and authentication, in subsequent posts.

Conclusion

The migration went swimmingly. Smashingly? Er… it went well. Smashing wasn’t penalized for its own success — when a popular article came along, they could serve the content consistently, no longer bailing over large loads. Along with this, the movement to a JAMstack infrastructure brought better performance and security.

Markus Seyfferth, former CEO of Smashing Magazine, noted:

“The time to first load is so much faster than before… before we had to wait for the HTML file being served for 800ms and now it’s 80ms.”

This process was successful and like any great engineering project, lessons were learned along the way. In this next article in this series, we’ll run through a tutorial and demo for what we would recommend given what we’ve learned. If you’d like to modernize your WordPress development and reap the performance and security benefits of JAMstack, read on!

Smashing Editorial(ra, vf, il)

from Tumblr https://ift.tt/2Gv3Lcu

Monday, 27 January 2020

Smashing Podcast Episode 7 With Stephanie Stimac And Aaron Gustafson: What’s New In Microsoft Edge?

Smashing Podcast Episode 7 With Stephanie Stimac And Aaron Gustafson: What’s New In Microsoft Edge?

Drew McLellan
A photo of Aaron Gustafson on the left, and Stephanie Stimac on the right
In this episode of the Smashing Podcast, I’m talking to Microsoft’s Aaron Gustafson and Stephanie Stimac to find out more about the brand new version of Microsoft Edge and how it’s affecting web developers worldwide as well as its impact on the web.

Show Notes

Weekly Update

Transcript

Drew McLellan: She’s a program manager and user researcher with the Microsoft Edge Developer Experiences Team where amongst other things she connects with web developers to identify problems in the web platform that they need solutions for to improve their day to day workflows.

Drew: He’s a web standards advocate, again with Microsoft, where he works closely with the Edge Browser Team as well as working with partners on progressive web apps and takes a focus on cross platform compatibility.

Drew: Together, they’re part of The Web We Want Team, which is a cross-browser vendor project to open a dialogue with web developers to help shape the future development of the web platform.

Drew: So we know they’re both from Microsoft, they’re both dedicated to making the web better for all of us, but did you know their most recent Web We Want meet up was hosted in a hot air balloon outside the Earth’s atmosphere before jumping back down to earth in the style of Felix Baumgartner.

Drew: My Smashing Friends, please welcome Stephanie Stimac and Aaron Gustafson.

Drew: Hello Stephanie, hello Aaron, how are you?

Aaron Gustafson: I’m smashing.

Stephanie Stimac: I am also smashing.

Drew: I wanted to talk to you today about some very interesting developments that have been going on with the Microsoft Edge browser. I guess most of us will know that Edge is traditionally the Windows 10 browser that replaced the much beloved Internet Explorer. Probably the first thing that we should note is the Edge isn’t exclusive to Windows 10 anymore, is it?

Aaron: Correct.

Stephanie: You can find Edge on Windows 10 and Mac OS. And then I’m not sure if it’s available on Windows Seven yet.

Aaron: Yeah, I believe it’s available on Windows Seven and Windows Eight now as well. And then of course there’s the mobile clients too for iOS and for Android.

Drew: So pretty much every modern platform.

Stephanie: Yep.

Aaron: Except for Linux, hopefully coming soon.

Drew: So even Linux desktop users can get in on the action.

Aaron: Eventually, that’s the hope.

Drew: Fantastic. So as far as I understand it, and correct me if I’m wrong here, but Edge was technically a continuation of the Internet Explorer code base, is that fair to say?

Aaron: Yeah, when Edge first started, it was basically kind of rip out all of the stuff that we realized was just kind of lagging around, things that had been proprietary and stuff like that. All of the old browser helper objects and activeX stuff and things like that. So it was massively gutted. It was kind of a chance for the team to really get rid of a lot of the legacy baggage and focus on the standard stuff. And so it was a continuation of the old Triton engine, but kind of refactored and made into the Edge HTML engine.

Drew: So it was like a massive home renovation inside the code base.

Aaron: Yeah, take it to the studs.

Drew: So I guess the biggest change that I want to talk about with the very newest version of Edge is that even more of those sort of older guts have been ripped out and replaced. And there’s a new rendering engine that’s been installed. Now I’m a little conscious of using jargon and people who aren’t maybe so familiar with the workings of browsers might not be completely happy with, what do we mean by a rendering engine. I think some of us who’ve been around and working on the web a long time will have picked up that information, even if we’re not working directly with the internals of browsers. But somebody who’s perhaps newer to web development might not understand, what do we mean by a rendering engine. So, what is a rendering engine inside a browser? And which bits aren’t a rendering engine?

Aaron: Well, there’s a bunch of different parts to a browser. The rendering engine is what we typically think of when we’re talking about what’s being actually painted to the screen and sort of the interactive layer of things. And so in the Chromium world, that is the Blink rendering engine.

Aaron: And then there’s the WebKit rendering engine, which is what runs Safari. And that was based on the KHTML rendering engine, which ran Konquerer back in the early days.

Aaron: The Trident rendering engine I mentioned before was the one on Internet Explorer for Windows. Tantek Celik had made the Tasman rendering engine as part of the team that was working on IE5 for Mac.

Aaron: So there are all these different rendering engines out there. And in fact I think there was a new one that Peter Paul Cox was just talking about called Flow. That’s like a multi-threaded rendering engine that’s based on SVG or something like that. And there’s a forthcoming browser for that that was designed for set top boxes, really low powered devices.

Aaron: But yeah, the rendering engines, taking all of your information and painting it to the screen. And then you’ve got your sort of underlying JavaScript engines as well. So v8 in the case of the kind of Chromium stack. And there are a bunch of different ones for that as well.

Drew: So when we say that the old rendering engine Trident, that came from IE and has been in Edge has been replaced. We’re actually talking about the bit that paints stuff to the screen and also associated things like the JavaScript engine. Is that fair?

Aaron: Yeah, in the case of the new Edge version, basically none of the underpinnings of old Edge are there. The Chromium project is an open source project that runs Chrome and Brave and a bunch of other, Opera and such. And that project includes the rendering engine, it includes the JavaScript engine, it includes all of the UI. It also includes the build process and all of that sort of stuff for actually creating the browser.

Aaron: And so we basically have the Chromium project, became the basis for the new Edge. And so we had to go through the process of making it look like Edge and adding in the functionality that we needed in terms of synchronization, using Microsoft accounts, all of that sort of stuff.

Aaron: So the new Edge does not share any lineage in kind of the traditional sense with the old Edge, which was Edge HTML, which inherited from Trident and so on and so forth. And you can keep going all the way back to Mosaic for most of these.

Aaron: But yeah, so now the new Edge is Chromium, which is based on WebKit before it, which is based on KHTML and so on and so forth. So it’s a completely different lineage than the previous one.

Drew: So, I mean, that’s a massive change isn’t it, for Microsoft to no longer have a browser that’s originated in its own code. But is actually taking an open source project and creating its own browser from that. Which I guess is similar to what Apple did back in the day with WebKit. What, was it KHTML they … ?

Aaron: Yeah, it was Konqueror was the … with a K.

Drew: Yes, Konquerer, so they took that open source project and that became Safari. And so Microsoft is doing a similar thing with the Chromium project and has made Edge out of it?

Aaron: Correct.

Drew: If I’m sat down in front of Edge as a web developer, what that I’m looking at is unique to Edge, what would I be seeing that I wouldn’t see if I was sat down in front of Chrome for example?

Stephanie: A lot of the features right now in this first stable release are consumer focused. So, there’s Collections which is coming out, which is a pretty cool way to pull stuff from the web into individual collections, hence the name. And you’re able to take that data and kind of put it into a side pane and then export it out to Word and Excel I believe. I’ve been using it for design projects and other things. It’s just cool for gathering resources.

Stephanie: And then one of the other features for Enterprise customers, and it’s only available on Windows, is IE mode. So that will actually render Internet Explorer within a tab in Edge for people who still need those pages that rely on old technology. So that’s pretty cool.

Stephanie: And then in terms of developer tooling, everything right now is the same. I know we have some stuff upcoming that is going to get ported up to Chromium, but there’s a cool 3D DOM viewer that’s in the works and the team’s been iterating on. And there are some other tools that don’t exist in Chrome yet that are coming. So that’s exciting.

Drew: So that’s a big deal for web developers, isn’t it? Because I think typically people get used to using a set of developer tools in their browser of choice and quite often frankly that is Chrome these days for a lot of web developers. So to be able to go across to Edge and find a very similar set of developer tools has to be quite a draw, do you think?

Aaron: Yeah, I think so. I mean it’s the sort of thing where you’re going to have the exact same tool set. We’re built on top of Chromium, including all of the dev tooling. We have been making a lot of investments in, as Stephanie was talking about, with the 3D DOM viewer. We’ve been making a lot of investments in terms of improving the overall accessibility of the dev tools, adding new tools into it, working on internationalization of the dev tools.

Aaron: And as Stephanie mentioned, the vast majority of the work that we’re doing, basically anything that is not UI specific to Edge or that involves our sync platform or things like that, all of that is being up streamed into Chromium. So the fixes that we make, the improvements that we make for accessibility are going to make Chrome better, are going to make Brave better, Opera better. They’re even going to make Electron better.

Aaron: So, it’s really neat to be kind of part of that larger ecosystem. And to be able to have impact beyond our own browser. And so dev tools has been an area we’ve been making a lot of investment. We’ve been doing a lot of work with partners in the Chrome team on progressive web apps as well. And trying to figure out what do progressive web apps, or what should they look like in desktop environments, because a lot of the focus has been on mobile.

Aaron: And then there’s also been a lot of work between the Dev Tools Team and the folks working on VS Code to make those tools work better together. So that you can basically access the dev tools from the browser from within VS code while you’re debugging your pages. And everything is kind of connected, like being able to click on a style rule in the Edge dev tools actually it will take you to that line in VS code in your style sheet. Which is mind blowing, it’s really cool stuff.

Drew: That sounds really exciting. I know VS Code is something that seems to be gaining an awful lot of traction in the web development community. I think it’s actually brought a lot of people back to Microsoft Tools who might have drifted away over the years. Certainly feels like a different era of Microsoft, as does this sort of embracing of open source and the new version of Edge. It all seems quite exciting and quite refreshing from a web development point of view.

Drew: Also from that point of view it seems like this is a really great convenience for web developers because if some of the major browsers, Chrome and Edge in particular and you mentioned Brave and Opera, what have you, are all using the same rendering engine, presumably that cuts down on masses of compatibility problems and browsers that don’t need to be tested in lots of different places. It should all just work the same, is that fair?

Stephanie: Yep.

Drew: Amazing.

Aaron: That’s the dream, right?

Drew: That is always the dream. It does raise a little bit of a concern though doesn’t it in the reduction of sort of healthy competition in the marketplace. Whereas we had Microsoft pushing their browser forward, Google pushing their browser forward. Apple hopefully ideally pushing their browser forward, Opera and all the other players. And that sort of creates this atmosphere of healthy competition where everyone’s trying to keep up with each other and trying to make improvements. If a lot of big players are all using the same rendering engine does that healthy competition suffer?

Aaron: This is something that I definitely have, having been a long time web standards person, kind of struggle a bit with, I don’t think … I totally get the business justification for it. From Microsoft’s standpoint it made a lot of sense. And from a front end dev perspective it’s nice to not have to cater to a bunch of different engines.

Aaron: I mean, on the whole, those of us who’ve been working on the web for a long time, have certainly seen a lot of convergence in terms of rendering. We don’t have as many problems as we had say back in the Netscape 4.7 days where we had just like, you know, I knew companies that were creating unique style sheets for each different browser which was just untenable.

Aaron: But I think what’s kind of different now is that back in the original browser wars you had all of these proprietary engines and everybody was kind of in a game of one-upsmanship in terms of trying to ship new platform features and new JavaScript features. Or in the case of Microsoft reverse engineering JavaScript in order to create J Script and trying to figure out how to fit it all together.

Aaron: But now we have the ability to actually work together in open source projects and still have the dialogue and still, I don’t know, fights not the right word, but to have serious discussions about the impact of different approaches. And to disagree with each other. And to really work on making specs really good. And to also have competing approaches to the underlying code within the context of say a Chromium project or WebKit or something of that nature or Mozilla in the Firefox space.

Aaron: So yes, on one hand we did lose another rendering engine. And I felt that same pain when Opera decided to go to Chromium. But I do feel somewhat heartened being inside Microsoft and seeing how committed we are to actually participating in the Chromium project in a meaningful way. And not just kind of sitting back and just accepting everything that comes downstream from Chromium, but actually kind of vetting what’s going into the platform and participating in that.

Aaron: So I’m a little bit heartened by that and feel like we’re not just there to take from that project and just accept whatever gets passed down by all of the different people who have a stake in that project. But to actually be collaborating in there as well.

Aaron: I think we still need to figure out what this means for standards bodies in terms of interoperable implementations. Because basically if you’ve got some browsers that decide they don’t want to implement something, they can totally stop something from becoming a standard. Even if all of the developers in the world wanted it.

Aaron: And it may not be anything like where they don’t agree with it. They just may not have the bandwidth to build the feature. Everybody’s pressed for staff and such.

Drew: I guess really the collaboration on the web as a platform has just moved locations from everybody agreeing to implement things the same in their individual code bases to some major players working together on the same code base essentially.

Drew: I mean, you mentioned briefly disagreements and obviously that’s something that could be a concern, I guess, in any open source project. But do we know how that will work, if developers from Google, for example, want to implement a feature and developers from Microsoft really don’t want to see that implemented. Do they just fight it out over GitHub issues?

Aaron: I mean, a lot of this stuff does happen in the open, either on GitHub issues or in the Crbug, which is the bug tracker for Chromium. I am not a browser engineer so I don’t know all of the details of the internals, but I believe there are a lot of things that we can basically turn off if we don’t want them.

Aaron: And I know Google has been working on sort of cleaning up or doing some housekeeping within the Chromium project to extract more of the Google related services and such up to the layer of converting it from Chromium to Chrome.

Aaron: And so we have a similar process taking Chromium and turning it into Edge. And obviously, we wouldn’t want to check in the design of the Edge UI for instance. Or the Collections thing, which is more of a UI user focus thing that Stephanie was mentioning, we’re not up streaming those into Chromium. Those exist in kind of the UI portion.

Aaron: So, as far as I understand it, as features come out of Chromium we have the ability to kind of turn off certain things that we may or may not like. And I know Brave does the same sort of thing because they have a much different approach to privacy than say Google does. And we may have a slightly different take than either of them or what have you.

Aaron: So I think there’ll be kind of things that happen at different points. Some of which will be kind of where the project gets converted into the browser from the open source project. Some of it will be within the standards organizations or interest groups within the standards organizations and so on.

Drew: And I guess it works the other way as well, doesn’t it? You mentioned a browser not having enough engineers on hand to develop a particular feature, traditionally the way things have worked within standards bodies is that we need multiple different implementations before something is accepted as part of the standard. Obviously that needs to be figured out if there aren’t that many implementations available to actually test with.

Drew: But, for example, if Microsoft wanted to implement a feature and Google decided that they just didn’t have the resources to implement that feature, it wasn’t high enough on their priority list, well, potentially they’re going to get it for free anyway because it just gets up streamed into the open source project. And then they can access it and all their users benefit without any of their engineers particularly needing to spend a significant time developing that.

Drew: I mean, if Mozilla decided that they were going to do the same with Firefox and just adopt the Chromium project, do you think that would be a problem for the web?

Stephanie: I mean, I would say yeah, because then we’re in this browser monoculture and honestly, I love Firefox. Firefox has some great tooling that Chromium doesn’t have. That would be, I think, really terrible to lose. And it’s kind of nice to have sort of an independent browser out there still fighting for the open web. I mean, we’re fighting for the open web too, but I think what Firefox is doing is still important.

Aaron: I mean, I kind of wonder what would happen if, let’s say, whether it’s Chromium or whether it’s WebKit or some fork of an existing open source project, what if a standards body kind of controlled the rendering engine and the JavaScript engine. And all of the browser makers were contributing to the development and kind of the ongoing creation of that. Would that necessarily be a bad thing or would it be a good thing? I don’t know. I don’t know what the answer is to that, but it’s kind of an interesting prospect that if … Browsers are not inexpensive and inconsequential to build, and especially rendering engines. It’s going to be probably relatively unlikely that we’re going to see a whole lot of new rendering engines and big investments in that area. Because so many companies have shifted to focusing on their services.

Aaron: With Apple, with all of its iCloud and its whole ecosystem. And Mozilla has been doing some similar things with their acquisition of Pocket and such. And then certainly there’s the Google suite of products, the Microsoft suite of products, etc. And Brave is focused around privacy and the basic attention token and that sort of thing. Everybody kind of has their little ecosystem of things.

Aaron: And so, I don’t know that there’s going to be a whole lot of interest in building out a new rendering engine and a new JavaScript engine to challenge the ones that are there. So, it’s kind of a question of will there continue to be kind of a consolidation. And if more people are actively interested in the ongoing development, and kind of having those difficult conversations about where the engine is going and where the web platform is going, could that be a good thing, I don’t know. It’s kind of an interesting thought experiment though.

Drew: It really is. The new Edge sounds really exciting. I’ve been playing around a little bit with the betas and enjoying that. How long do you think it will take for this new version to roll out to the world? Because it’s, I mean, only just been released this month, hasn’t it, we’re January 2020.

Aaron: Yep, January 15th, it went to general audience basically. So, we’ve had the Canary dev and beta versions. Gosh, when did we start rolling this out, Stephanie, do you remember?

Stephanie: I’m going to say six months ago, but I honestly, I’m just throwing that out there. I feel like I’ve been using Canary as my daily driver for what feels like months.

Aaron: Yeah, I think we’re kind of skewed because we started using it internally for a bit before we started to open it up to the public. But yeah, I think I want to say we started sharing it at Build last year, which would have been May, I think is when the Canary, and maybe the diversion route, I don’t remember if beta was out then. But yeah, the fully stable version came out on the 15th.

Drew: So, you’re both involved in a project called The Web We Want, which is a sort of initiative between lots of different browser makers, is that right?

Stephanie: Yeah, so The Web We Want, I think Aaron started it last June or so and we started talking about it. And yeah, The Web We Want is basically an initiative and Aaron and I have been the primary ones kind of running it, but we’ve had interest from other browser vendors and folks who work on those teams.

Stephanie: And it’s basically just a way to gather feedback from developers about problems that they’re having on the web. Because we, as people who build the browser, can kind of focus on those new shiny things that we want to implement. But developers, as we’ve come to see through this initiative, there’s a lot of stuff that they’re still struggling to build that isn’t native in the browser. That probably should have been solved by now, but hasn’t.

Stephanie: And so that’s kind of what the initiative is all around, finding out what those problem areas are and then taking those back to browsers and standards bodies if it’s that sort of problem.

Drew: So, what sort of form does that take? Is it something you do in person or is it all online?

Stephanie: So, we have two components to it. So you can go to webwewant.fyi and you can fill out a form with your problem and your use case that you’ve encountered on the web. And then Aaron and I have been, I think we went, well, I think we’ve hosted six or seven events within a conference or a meet up.

Stephanie: And so the form that that takes is we usually have a 45 minute to hour long session at a conference we’re partnering with. And we give people who have submitted their problems that they’ve encountered on the web a chance to present those to a panel of judges who are usually industry experts.

Stephanie: And in between we usually have four or five of those problems presented during a session. And in between each of those we have our panel of judges kind of talk about the problem space that’s being presented. And whether or not it’s an interesting problem space. Or if they think it would be useful for browser vendors to go work on that.

Stephanie: And then at the end of that session we have our judges pick what they think is the most pressing problem to go and fix. And we also have an audience component, so the audience for the session also gets to vote on what they think is the most pressing thing that browser vendors should go fix.

Stephanie: And then we take that data back to our teams. And we’re currently in the process of figuring out how to disperse that out among the different browser teams.

Drew: Are there any sort of standouts, suggestions that you remember that have particularly stuck with you?

Stephanie: So, there’s been a couple, the one that keeps sticking out for me though is HTML Controls keep coming up. And that’s sticking out for me because I’m working on a talk right now that involves HTML Controls and how just not being able to style them or extend them is an issue. And so it’s kind of cool to see that feedback reiterated even more.

Stephanie: There’s been some really interesting, there was one for a source view order or source order viewer in the dev tools that had to do with, I believe, seizes grid content and seeing, because you can kind of move things out of order, and having that kind of identify how things flow through the DOMs. So, I think that would be a really cool thing to get implemented in the dev tools.

Aaron: Yeah, there’s been a lot of accessibility related submissions which has been really neat. Another one that was dev tools and accessibility related was actually, rather than sort of relegating accessibility testing to its own part of the, was it the audits tab, I think it’s where it currently, like lighthouse does some accessibility testing, or you could use web hint or accessibility insights and such.

Aaron: The idea of this is to actually start capturing some of the low hanging fruit in terms of accessibility errors and bubbling that up in the console for instance. So, if it sees that you have some images that are missing alt text or some really tiny fonts or not enough contrast, it could actually throw those as errors in the console. And that tends to be where a lot of developers are checking to see if their stuff is operating properly. So that would make it a little bit more obvious to folks that they have accessibility issues that they need to address.

Aaron: And you could see some similar ways to incorporate that into the Dom viewer or the CSS inspector tools where it could flag something as being too low contrast or highlight an element as having some accessibility issue that’s known. That sort of thing would be pretty interesting.

Stephanie: Yeah, I want to piggyback off that. There was actually a really interesting one presented at View Source in Amsterdam last year. It was around the browser automatically fixing certain types of accessibility issues for you. And just with color and contrast or maybe certain fonts being unreadable. And the proposal and the case study presented was really, really well thought out. But it also kind of poses this interesting question of if the browser starts to fix things for you as a developer, are you going to write bad code because you don’t have to worry about it.

Stephanie: So it was interesting because the ideas that he put forward and kind of demoed were really cool. But that’s kind of what does that mean for developers so, are they going to get lazy. So, I’ve seen some cool stuff come out of it.

Drew: So I guess it’s still pretty early days, isn’t it, for the initiative. So I guess we’ve not seen anything go as far as actually reaching even early development stages with any browsers. Do you know what that process will look like, you said you were still sort of trying to figure it out, is that right?

Aaron: Yeah, we’re all still trying to figure it out, but we do have the right people involved. We’ve got folks from Mozilla and from Google and from Igalia as well, they were the ones who did a lot of the grid work and they’re very familiar with how all of this standard stuff works. In fact, I think they’re doing Methanol right now, doing the implementation for WebKit.

Aaron: So yeah, we’ve got a bunch of people on board that are keen to work on this and keen to see it be successful. And the W3C is paying attention to it as well, which is cool.

Stephanie: So, for us right now, we’ve taken the, I think we have 12 top, we took the winners from each event last year, and I’ve kind of bucketed them out into their specific categories. So some are dev tools focused, some are HTML, web platform focused. And then I can’t remember what the third category was, it was just kind of general.

Stephanie: And so on the Edge side we’re starting to at least look into, I’ll have a couple of Twitter surveys going out actually hopefully sometime soon to help gather more feedback to validate that these are problem spaces that we should invest time in. And then hopefully we’ll go from there. So I’m hoping sometime this year we’ll start to see some sort of progress. But it is a little bit slow at the moment.

Drew: It’s great because the web actually is a very collaborative platform. And I think as a web developer just building sites on the day to day, you forget that there’s actually ways to feed back into the process and ways to make your voice heard. And I think this sounds like a really great, easy way to make your voice heard.

Drew: If any of our listeners wanted to come along to a Web We Want session, are there any coming up?

Stephanie: There are a couple, so let me pull up my list here. So, well, Open Source Festival in Lagos, Nigeria. Aaron and I won’t be there, but they’re actually running that event independently.

Stephanie: And then I will be at SF HTML5 in San Francisco at the end of March. I believe that’s free, but don’t quote me on that. I have to check. So if you’re in the San Francisco area, we’re running it there.

Stephanie: And then SmashingConf San Francisco in April. We will be running it there.

Stephanie: And hopefully TBD, still looking at SmashingConf Austin.

Stephanie: And I believe that’s all we have on the calendar right now. But, going to do a little plug here, if you run a conference or a meet up and would like this community focused session to be run there, please reach out because it’s really cool.

Drew: Of course, the SmashingConfs are the best ones to go to we think here.

Stephanie: Yes, yes they are.

Drew: So, to me that sounds all really, really exciting. And I’m heartened to see all the big browser makers actually coming together and backing that sort of initiative to get out and to talk to web developers, talk to the people who are actually solving these sort of everyday problems, building websites and apps for their clients and for their companies.

Drew: I think it’s really important to actually listen to the people who are at the coalface doing the work.

Drew: I normally ask at the sort of end of these episodes, what have you been learning lately? Because we’re all about learning stuff here at Smashing, but really with Web We Want, I think you must be learning all these things all the time by speaking to web developers. So I really think we’ve covered masses there.

Drew: Is there anything else that you wanted to mention about Web We Want or about the new version of Edge that we hadn’t talked about?

Aaron: I just think if folks have ideas, even just the germ of an idea, some cow path that you think should be paved for the web, reach out to us, let us know. We’re happy to also work with you on kind of refining the submission. So, in a lot of cases we’ll get a submission that’s kind of a germ of an idea. It’s not perfectly articulated or anything like that. And we work with those authors to make sure that we kind of capture what it is that they’re looking to put out there. So, don’t worry about your grammar or your spelling or anything like that. It’s not like we just post it up as soon as you send it, we’ll look at it, review it.

Aaron: And in some cases people have suggested things that actually exist, in which case we tell them, hey, you can actually do that and here’s a way to do that. So we can, it’s a little bit of stack overflow as well. Because he can tell you if there’s something that you can do that already with standards.

Aaron: But yeah, I mean, we want any and all ideas on how we can improve the web. Hopefully they’re actionable ideas. In some cases we get like, the web is too hard. And it’s like, you know, I feel you, but, you know, not something browser vendors can really solve.

Aaron: But yeah, I mean, we want to know what is it that you’re doing, where you’re running into problems. We have the kind of saying, if you could wave a magic wand and fix something on the web, what would it be?

Aaron: And yeah, so you can can hit us up via the forum. Stephanie mentioned webwewant.fyi. Or we’re webwewantfyi on Twitter. Yeah, please reach out.

Stephanie: I will also pile on just to talk something about Edge that also, the team is super hungry and eager to get feedback from all of our users. So if you’re in Edge and there’s something you don’t like or there’s something you love, there’s a little feedback icon you can click. All of the teams see that feedback and are looking at it. And if you have a problem are very proactive and engaging. And you can always ping MS Edge Dev on Twitter if you have a problem and it hasn’t been addressed yet. So trying to be super proactive and really build a browser that not only developers want to use, but the world.

Drew: If you, dear listener, would like to hear more from Aaron or Stephanie, you can follow them on Twitter where he’s @AaronGustafson and she’s @seaotta. You can find The Web We Want at webwewant.fyi.

Drew: Thank you for joining us today Aaron and Stephanie, do you have any parting words?

Stephanie: Thanks.

Aaron: Adios.

Smashing Editorial
(dm, ra, il)

from Tumblr https://ift.tt/3aH1wR8

How To Design Powerful Narratives On Mobile

How To Design Powerful Narratives On Mobile

Suzanne Scacca

Stories don’t always have to be told in a Once Upon a Time… format. Take a look at movies, for instance. Some stories are told in chronological order while others jump around a timeline. There are stories told from the perspective of the protagonist while others let viewers see the story from everyone’s point of view. Plus, stories can be sad, funny, dramatic or all of the above.

There’s a ton of variation in how a story can be told. There is one thing they all need to have in common though:

They need to somehow draw the reader into the narrative.

Storytelling is a very powerful tool for websites, but the same rules apply. It doesn’t matter how you tell the story — it just needs to draw the visitor in.

Today, I’m going to walk you through various storytelling techniques and give you some ideas for how you can design these kinds of narratives into your own mobile websites or PWAs.

How To Design Powerful Narratives On Mobile

Storytelling is a great tool for mobile websites.

One reason for that is because the smaller screen forces you to be more concise with the narrative. That means you can get visitors more quickly to the climax of your story, which will speed up their time to conversion.

Another reason why storytelling works so well on mobile is because it’s easier to suck visitors into the story. All you need to do is give your site a book-like feel and they’ll become quickly immersed in it.

What’s more, you can really let the visuals do most of the talking and cut down on the lengthy stories that company founders would otherwise want to tell about their companies.

There’s one industry, in particular, that I think does an exceptional job of storytelling in this manner, and that’s the beer and alcohol industry. If you think about it, these kinds of companies can’t afford to simply push their products in consumers’ faces. Here are some reasons why:

  1. “Let’s get you intoxicated” isn’t a very strong or unique selling point. But a story that draws the customer in and makes them part of the narrative can be.
  2. To prevent the analysis paralysis that comes with choosing one kind of liquor or beer from dozens of similar options, brands use stories to differentiate themselves.
  3. You can more effectively build long-term and loyal relationships with customers by turning your products into one-of-a-kind experiences.

Bottom line: When building sites for products or services in highly competitive spaces, storytelling can be a real game changer. As far as how you accomplish that through design, let’s look at some of the more common storytelling techniques and examples that show how they’re pulled off.

Aviation Gin

What kind of story does the site tell?
The Guided Journey

What kinds of businesses benefit from this kind of storytelling?
This kind of storytelling technique is best for new companies that have a strong branding component. That way, the company isn’t just another lookalike business entering the space. It’s offering something more entertaining and engaging that encourages prospective customers to hop along for the ride — even on Day 1.

The “mascot” is one of the best ways to make this kind of storytelling work because they become the visitors’ guide to exploring the site and all it has to offer.

Think about Flo from Progressive, William Shatner for Priceline or The Old Spice Guy. The second you see these mascots, you can’t help but wonder what kind of “adventure” they’re going to take you on next. It’s a fantastic way to build a loyal following.

What does The Guided Journey storytelling method look like?
In the case of Aviation Gin, co-owner Ryan Reynolds has not only lent his star power, but also the edgy sense of humor he’s well known for to the brand.

Upon entering the mobile site, Reynolds’ face is the first thing visitors see:

Ryan Reynolds video on Aviation Gin mobile site
Ryan Reynolds introduces mobile visitors to Aviation Gin with his trademark humor. (Source: Aviation Gin) (Large preview)

The video (and the ridiculous quote beneath it) is what you’d expect from Reynolds, cracking jokes while introducing visitors to an absurd world he probably made up on the spot. It’s also short, to the point and will undoubtedly leave visitors thinking:

Okay, okay, I gotta give this gin a try.

Scroll down once, and you’ll find this invitation to connect with Reynolds through the Aviation Gin newsletter:

Aviation Gin - Ryan Reynolds
Aviation Gin uses Ryan Reynolds as a pilot for its brand journey. (Source: Aviation Gin) (Large preview)

Although it cuts back a bit on Reynolds’ humor, it continues to portray him as the pilot of this journey visitors are about to take. And if we know anything about what our users want, that’s a brilliant move. Reynolds isn’t just dangled in front of them with the occasional ad (like so many companies do with their spokespeople). He’s actually built into the experience.

What’s more, Aviation Gin has constructed the rest of the website to play out like a flight, with crafty plays on words and relevant graphics.

Aviation Gin flight-related wordplay and imagery
Aviation Gin plays up its brand name and guides visitors through the site with flight-related wordplay and imagery. (Source: Aviation Gin) (Large preview)

This one-page website is done so well from start to finish, and a huge part of that is due to the story they’ve committed to:

  • We are Aviation Gin.
  • Ryan Reynolds will be taking you to your destination.
  • Don’t you want to come along for the ride?

It’s a great narrative. And for those who don’t know who Ryan Reynolds is, the buttoned-up website with its flight analogies and wordplay should be enough to draw them in, too.

Pacto-Navio

What kind of story does the site tell?
The Immersive Tale

What kinds of businesses benefit from this kind of storytelling?
This is another effective approach to storytelling if you’re building a site for a new business. In this case, though, there isn’t a real-life persona who can take them along on the brand’s journey. Instead, you have to rely on the strength of the brand and your ability to design something that feels immersive.

This is going to be especially useful if the company hasn’t been around for a long time and you don’t have much history or accolades to draw upon. Just make sure you have a clear focus for your tale, so visitors don’t try to wander off the path you’ve created for them.

What does The Immersive Tale storytelling method look like?
To build an immersive tale on a mobile website, you have to frame it as an adventure. That way, visitors aren’t even thinking about the menu button. Instead, they’re ready to play along and let you take them through the site.

Pacto-Navio handles this kind of storytelling well. There are a number of reasons why this works, too.

First, each part of the story has a dedicated full page:

Pacto-Navio home page animation
Pacto-Navio introduces visitors to the mobile site with an animation of the West meeting the East. (Source: Pacto-Navio) (Large preview)

This, for instance, is the home page. At first, all visitors see is the compass, but then the two hands move in from the east (France) and the west (Cuba) to symbolize the two countries where the rum comes from.

This is another reason why this site feels so immersive. From the very beginning, the website actively moves visitors from the point of entry to their destination. And I mean this literally.

The site is full of animations. They’re not overwhelming or unnecessary. Each of the moving elements (even the transitions between horizontal page flips) is subtle yet relevant.

Pacto-Navio storybook pages
Pacto-Navio website visitors stay engaged with the story through subtle animation and page-flips. (Source: Pacto-Navio) (Large preview)

The design of the site as a whole should make visitors feel like they’re reading some epic tale of adventure. But this is a website; not some book that’s confined to telling a story on the pages.

The Pacto-Navio mobile site allows visitors to decide when they want to stop and explore some more. And when they do, more of the story is revealed:

Pacto-Navio story
Pacto-Navio’s mobile website feels like an adventure book. (Source: Pacto-Navio) (Large preview)

In a sense, this style of website — where each page is represented by a single card and they can be flipped through like the pages of a book — is one of the most engaging ways to tell a story.

Each page and corresponding graphic is served up one at a time. Then, when visitors choose to explore more, the story is briefly recounted while small animations keep them focused on moving down the page. It really does feel like you’re reading a book.

Samuel Adams

What kind of story does the site tell?
The Love Letter

What kinds of businesses benefit from this kind of storytelling?
If you’ve ever read a book or watched a movie where the physical location (usually a city) has a role to play in the story, then you know how emotional of an experience it can become.

Take any of Woody Allen’s movies, for instance. Many of them are set in New York City or in lush cities around Europe — and it feels as though the cities themselves are characters. The same goes for books. Stephen King does this a lot, like with the Overlook Hotel in The Shining or Shawshank Prison in The Shawshank Redemption. The places play just as important a role as the people in the stories.

Believe it or not, this is something you can do with your mobile site if it’s for a business with strong ties to a geographic area.

What does The Love Letter storytelling method look like?
Samuel Adams is a brewery with deep ties to Boston, Massachusetts.

People from Boston have a tendency to be fiercely loyal champions for local brands and businesses — and the relationship they have with this brewery is no different. That’s why it’s no surprise that Samuel Adams’ website is dedicated to:

  1. The city of Boston,
  2. The people of Boston.

Think of it as a love letter to all things Boston.

The mobile site starts with the following:

Samuel Adams Toast Someone campaign
The Samuel Adams mobile site encourages visitors to “Toast Someone”. (Source: Samuel Adams) (Large preview)

There’s no mention of Samuel Adams’ beer in this hero image. This is a call-to-action: Toast Someone.

This video shows comedians toasting the people who changed their lives and empowered them to do what they do. It’s a really heartfelt way to start people on their journey with Samuel Adams.

A little way down the page, the brewery explains why it’s had comedians make these toasts in the first place:

Samuel Adams Toast Someone Project
Samuel Adams dedicates its home page to its Toast Someone Project. (Source: Samuel Adams) (Large preview)

But it’s not just famous people that the brand has asked to participate in its collective toast. The next video is an ode to the city of Boston and its residents.

Samuel Adams Boston residents toast
Samuel Adams has well-known residents give a toast in this home page video. (Source: Samuel Adams) (Large preview)

Visitors see famous Boston residents (like Big Papi of the Boston Red Sox give their love and respect to the city), but it’s not just big-name figures who speak out in the video:

Samuel Adams Boston’s real faces
Samuel Adams shows off the real faces of Boston residents in this Toast video. (Source: Samuel Adams) (Large preview)

In addition to seeing beautiful shots of the city of Boston, you can see the real people of the city. In the screengrab above is one of the survivors of the Boston Marathon bombing. But you can also see a team of rowers heading onto the Charles River, a barbershop and people boxing in a gym.

Sure, Sam Adams’ beer bottles can be seen in the toasts people give, but it’s still a touching video and a beautiful ode to the city of Boston. And because this site is completely separate from the Samuel Adams store, it helps keep people’s focus on the toast project and the positive sentiment coming from it.

Wintrip Brew Co.

What kind of story does the site tell?
The Graphic Novel

What kinds of businesses benefit from this kind of storytelling?
There are tons of examples of graphic novels and comic books that have later become hit movies or TV shows:

  • The Walking Dead
  • Watchmen
  • Sin City
  • V for Vendetta
  • Ghost in the Shell

Truth be told, this style of storytelling won’t work for most brands because it requires a brand to be edgy, dark or boundary-pushing for it to work. It also needs a brand to be 100% focused on its product. That doesn’t mean it can’t have an original or inspiring story of its own to tell, but the pacing of a story like this should really stay focused on what you’re selling.

If that’s something you’re working with and you have the creative chops to design a site that represents the quick-paced and dramatic pages of a graphic novel, it’s a helluva way to capture and hold your audience’s attention.

What does The Graphic Novel storytelling method look like?
There’s no one way to design websites using this storytelling method. That’s because there’s so much variation in the way graphic novels and comic books are designed. Batman, for instance, is dark and moody. Watchmen is all about the exaggerated, retro design. And, then, there’s The Walking Dead that just feels grimy.

You have to decide what kind of “edge” you want to give your site. But one thing is for certain: it needs to have a striking contrast between dark and light.

This is the mobile site for Wintrip Brew Co.:

Wintrip Brew Co. mobile site
Wintrip Brew Co. welcomes visitors to its mobile site with a desolate-looking graphic novel design. (Source: Wintrip Brew Co.) (Large preview)

If you didn’t know any better, you might think this was a site for a graphic novel or even a video game.

But this is indeed the way Wintrip Brew Co. welcomes its visitors — and it’s a great first impression to make. After all, who the heck would go to the trouble of designing a home page to look like a graphic novel if the rest of the site weren’t going to follow suit?

There’s no reason why visitors wouldn’t want to keep stumbling along this path.

Wintrip Brew Co. characters
The Wintrip Brew Co. website has its own cast of unique and odd characters. (Source: Wintrip Brew Co.) (Large preview)

Every inch of this site feels like a graphic novel. There are odd characters scattered throughout it, inviting visitors to explore more of each page (like the zombie-eyed chef above).

Then, there’s the actual story told on the pages:

Wintrip Brew Co. typography
Wintrip Brew Co. keeps typography design simple so the attitude in the words can stand out. (Source: Wintrip Brew Co.) (Large preview)

The typography is kept simple and yet the striking color contrast gives it an edge. If visitors don’t pick up on the subtle cues the font design gives off, then the words that are chock full of attitude certainly will.

While I’d love to show you how the “Beer” page is designed, the words on it are incredibly risque. Suffice to say, what you see above is the tip of the iceberg in terms of boundary-pushing.

But that’s okay because that’s what this brand is all about. It’s evident even in the way they’ve brewed their beers that they want to appeal to beer drinkers who are looking for something darker and more cutting-edge. And the design of this website has set the perfect tone for that.

Yuengling

What kind of story does the site tell?
The Timeline

What kinds of businesses benefit from this kind of storytelling?
The Timeline is best used by brands that have lengthy histories. These are the companies that have been in operation for over 100 years. It would also apply to businesses that have dozens of milestones they’ve racked up over the years.

You might not get many of these that pass your way, but when you do, The Timeline is a beautiful way to tell a company’s (and their loyal patrons’) story.

As for why you’d opt for this method of storytelling, it’s one of the more efficient ways to do so. Otherwise, you’d end up with insanely long pages trying to recap everything a brand has accomplished over the years — and that would be a waste of your time trying to design something like that and a major test of your visitors’ patience.

Instead, a timeline chops up a story into small, digestible and still very impressive pieces.

What does The Timeline storytelling method look like?
This one is obvious. You’re going to start with the very beginning of the brand’s story (usually how the founders met or came up with their idea) and end with today. Along the way, you’re going to share a few words and some eye-catching imagery to highlight the milestones.

Let’s look at the example of Yuengling, which is the oldest brewery in the United States. The company’s timeline begins with a single introductory paragraph and video:

Yuengling mobile site story
The Yuengling mobile site introduces new and old customers to its story. (Source: Yuengling) (Large preview)

Visitors who want to take time to get this introduction can do so with ease. The rest can skip down to the timeline that starts with the company’s founding in 1829.

The Yuengling timeline begins
Yuengling tells its story with eye-catching visuals and short snippets of text on a timeline. (Source: Yuengling) (Large preview)

What’s always nice about a company’s timeline is being able to see how consistent it’s been from the very beginning. Even if a company has changed hands or undergone name changes, many times the design of its branding or the product itself remains unchanged.

You can see a hint of that above with the Porter design, which isn’t unlike the current design of some of Yuengling’s packaging.

As the story goes on, visitors get to learn more about the company — its wins, its losses, and the changes it’s made for the sake of its customers.

Yuengling tells an honest story
Yuengling uses its website’s timeline to show customers how they’ve factored into the company’s decisions. (Source: Yuengling) (Large preview)

That’s ultimately what the goal should be in designing a brand’s story in a timeline. It shouldn’t be so you can display award after award received. If you want to keep visitors engaged and turn them into customers in the end, then you have to show them how they factor into the story.

In the screenshot above, 1996’s summary says:

Demand for Yuengling actually begins to exceed the brewery’s capacity. The decision is made to withdraw from markets outside of the local area.

This is exactly the kind of thing customers want to see when they’re looking for brands to stay loyal to. This demonstrates the brand’s loyalty to providing customers with a high-quality product instead of on getting rich (which it could’ve easily done).

Wrapping Up

As you can see, there are tons of ways to tell a story through design. And while the websites above look fine when viewed on larger screens, where they really shine is mobile. That’s because designers are able to craft experiences that feel more like you’re turning the pages of a book instead of being asked to scroll through endless amounts of text on the web.

The above list is by no means exhaustive as you can tell stories in all kinds of ways for businesses. Just listen to what your clients have to say about their companies and then create a journey that matches the one they took (or the detour they’re on today).

Related Article on SmashingMag

Users have many expectations when they come to your site. Placing your users in the context of a story helps you understand those expectations and their motivations. You’ll see what they want and expect, but also what they need. Once you know their needs, you can meet those needs. Learn more from an article written by John Rhea →

Smashing Editorial
(ra, yk, il)

from Tumblr https://ift.tt/2t0mycu