Tag Archives: project

Thumbnail

BEM For Beginners: Why You Need BEM




BEM For Beginners: Why You Need BEM

Inna Belaya



BEM makes your code scalable and reusable, thus increasing productivity and facilitating teamwork. Even if you are the only member of the team, BEM can be useful for you. Nevertheless, many developers believe that such a system approach like BEM puts additional boundaries on their project and makes your project overloaded, cumbersome, and slow.

We’ll be collecting all of the main aspects of BEM in a condensed form. This article helps you understand the basic ideas of BEM in just 20 minutes, and to reject prejudices that the system approach is detrimental to your project.

The Big BEM consists of Methodology, Technologies, Libraries, and Tools. In this article, we’ll talk more about the methodology itself because it is the concentrated experience of a huge number of developers and it brings a systematic approach to any project.

In order to show you some practical cases of BEM, we’ll touch on the BEM technologies and completely skip the libraries and tools.

From theory to practice:

So, is BEM a hero or a villain? It’s up to you! But first, read the article.


BEM as a Batman logo


BEMBatman

The Main Reasons Why We Do Not Use Any Selectors Except Classes

One of the basic rules of the BEM methodology is to use only class selectors. In this section, we’ll explain why.

  • Why don’t we use IDs?
  • Why don’t we use tag selectors?
  • Why don’t we use a universal selector?
  • Why don’t we use CSS reset?
  • Why don’t we use nested selectors?
  • Why don’t we combine a tag and a class in a selector?
  • Why don’t we use combined selectors-
  • Why don’t we use attribute selectors?

We Don’t Use IDs (ID Selectors)

The ID provides a unique name for an HTML element. If the name is unique, you can’t reuse it in the interface. This prevents you from reusing the code.

Common Misconceptions
  1. IDs are required for using JavaScript.
    Modern browsers can work with either IDs or classes. Any type of selector is processed at the same rate in the browser.
  2. IDs are used with the <label> tag.
    If you place <label> inside a control, it doesn’t need an ID. Instead of <input id="ID"><label for="ID">Text</label>, simply use <label><input type="...">Text</label>.

We Don’t Use Tag Selectors

HTML page markup is unstable: A new design can change the nesting of the sections, heading levels (for example, from <h1> to <h3>) or turn the <p> paragraph into the <div> tag. Any of these changes will break styles that are written for tags. Even if the design doesn’t change, the set of tags is limited. To use an existing layout in another project, you have to solve conflicts between styles written for the same tags.

An extended set of semantic tags can’t meet all layout needs, either.

An example is when the page header contains a logo. A click on the logo opens the main page of the site (index). You can mark it up with tags by using the <img> tag for the image and the <a> tag for the link.

<header>
  <a href="/">
    <img src="img.logo.png" alt="Logo">
  </a>
</header>

To distinguish between the logo link and an ordinary link in the text, you need extra styles. Now remove underlining and the blue color from the logo link:

header a 
  ...

The logo link doesn’t need to be shown on the main page, so change the index page markup:

<header>
  <!-- the <a> tag is replaced with <span> -->
  <span>
    <img src="img.logo.png" alt="Logo">
  </span>
</header>

You don’t need to remove the underlining and the blue color for the <span> tag. So let’s make general rules for the logo link from different pages:

header a,
header span

  ...

At first glance, this code seems all right, but imagine if the designer removes the logo from the layout. The selector names don’t help you understand which styles should be removed from the project with the logo. The “header a” selector doesn’t show the connection between the link and the logo. This selector could belong to the link in the header menu or, for example, to the link to the author’s profile. The “header span” selector could belong to any part of the header.

To avoid confusion, just use the logo class selector to write the logo styles:

.logo 
  ...

We Don’t Use CSS Reset

CSS reset is a set of global CSS rules created for the whole page. These styles affect all layout nodes, violate the independence of components, and make it harder to reuse them.

In BEM, “reset” and “normalize” aren’t even used for a single block. Resetting and normalization cancel existing styles and replace them with other styles, which you will have to change and update later in any case. As a result, the developer has to write styles that override the ones that were just reset.

We Don’t Use The Universal Selector (*)

The universal selector indicates that the project features a style that affects all nodes in the layout. This limits reuse of the layout in other projects:

  • You have to additionally transfer the styles with an asterisk to the project. But in this case, the universal selector might affect the styles in the new project.
  • The styles with an asterisk must be added to the layout you are transferring.

In addition, a universal selector can make the project code unpredictable. For example, it can affect the styles of the universal library components.

Common styles don’t save you time. Often developers start by resetting all margins for components (* margin: 0; padding: 0; ), but then they still set them the same as in the layout (for example, margin: 12px; padding: 30px;).

We Don’t Use Nested Selectors

Nested selectors increase code coupling and make it difficult to reuse the code.

The BEM methodology doesn’t prohibit nested selectors, but it recommends not to use them too much. For example, nesting is appropriate if you need to change styles of the elements depending on the block’s state or its assigned theme.

.button_hovered .button__text

  text-decoration: underline;

.button_theme_islands .button__text

  line-height: 1.5;

We Don’t Use Combined Selectors

Combined selectors are more specific than single selectors, which makes it more difficult to redefine blocks.

Consider the following code:

<button class="button button_theme_islands">...</button>

Let’s say you set CSS rules in the .button.button_theme_islands selector to do less writing. Then you add the “active” modifier to the block:

<button class="button button_theme_islands button_active">...</button>

The .button_active selector doesn’t redefine the block properties written as .button.button_theme_islands because .button.button_theme_islands is more specific than .button_active. To redefine it, combine the block modifier selector with the .button selector and declare it below the .button.button_theme_islands because both selectors are equally specific:

.button.button_theme_islands {}
.button.button_active {}

If you use simple class selectors, you won’t have problems redefining the styles:

.button_theme_islands {}
.button_active {}
.button {}

We Don’t Combine A Tag And A Class In A Selector

Combining a tag and a class in the same selector (for example, button.button) makes CSS rules more specific, so it is more difficult to redefine them.

Consider the following code:

<button class="button">...</button>

Let’s say you set CSS rules in the button.button selector.
Then you add the active modifier to the block:

<button class="button button_active">...</button>

The .button_active selector doesn’t redefine the block properties written as button.button because button.button is more specific than .button_active. To make it more specific, you should combine the block modifier selector with the button.button_active tag.

As the project develops, you might end up with blocks with input.button, span.button or a.button selectors. In this case, all modifiers of the button block and all its nested elements will require four different declarations for each instance.

Possible Exceptions

In rare cases, the methodology allows combining tag and class selectors. For example, this can be used for setting the comments style in CMS systems that can’t generate the correct layout.

You can use the comment to write a text, insert images, or add markup. To make them match the site design, the developer can pre-define styles for all tags available to the user and cascade them down to the nested blocks:

<div class="content">
  ... <!-- the user’s text -->
</div>
CSS rules:
.content a 
  ...

.content p 
  font-family: Arial, sans-serif;
  text-align: center;

We Don’t Use Attribute Selectors

Attribute selectors are less informative than class selectors. As proof, consider an example with a search form in the header:

<header>
  <form action="/">
    <input name="s">
    <input type="submit">
  </form>
</header>

Try using selector attributes to write the form styles:

header input[type=submit],
header input[type=checkbox] 
  width: auto;
  margin-right: 20px;

header input[type=checkbox] 
  margin: 0;

In this example, you can’t tell for sure from the selector name that the styles belong to the search form. Using classes makes it clearer. Classes don’t have restrictions that prevent you from writing clearly. For example, you can write it like this:

.form .search 
  ...

Now the code is less ambiguous, and it’s clear that the styles belong to the search form.

But the nested selectors still make the CSS rules more specific and prevent you from transferring the layout between projects. To get rid of nesting, use BEM principles.

Summary: class is the only selector that allows you to isolate the styles of each component in the project; increase the readability of the code and do not limit the re-use of the layout.

CSS styles isolation is the most frequent start point of the BEM journey. But this is the least that BEM can give you. To understand how isolated independent components are arranged in BEM, you need to learn the basic concepts, i.e. Block, Element, Modifier, and Mix. Let’s do this in the next section.

The Basics Of BEM

Block And Elements

The BEM methodology is a set of universal rules that can be applied regardless of the technologies used, such as CSS, Sass, HTML, JavaScript or React.

BEM helps to solve the following tasks:

  • Reuse the layout;
  • Move layout fragments around within a project safely;
  • Move the finished layout between projects;
  • Create stable, predictable and clear code;
  • Reduce the project debugging time.

In a BEM project, the interface consists of blocks that can include elements. Blocks are independent components of the page. An element can’t exist outside the block, so keep in mind that each element can belong to one block only.

The first two letters in BEM stand for Blocks and Elements. The block name is always unique. It sets the namespace for elements and provides a visible connection between the block parts. Block names are long but clear in order to show the connection between components and to avoid losing any parts of these components when transferring the layout.

To see the full power of BEM naming, consider this example with a form. According to the BEM methodology, the form is implemented using the form block. In HTML, the block name is included in the class attribute:

<form class="form" action="/">

All parts of the form (the form block) that don’t make sense on their own are considered its elements. So the search box (search) and the button (submit) are elements of the form block. Classes also indicate that an element belongs to the block:

<form class="form" action="/">
  <input class="form__search" name="s">
  <input class="form__submit" type="submit">
</form>

Note that the block’s name is separated from the element’s name with a special separator. In the BEM classic naming scheme, two underscores are used as a separator. Anything can work as a separator. There are alternative naming conventions, and each developer chooses the one that suits them. The important thing is that separators allow you to distinguish blocks from elements and modifiers programmatically.

Selector names make it clear that in order to move the form to another project, you need to copy all of its components:

.form__search {}
.form__submit {}

Using blocks and elements for class names solves an important problem: It helps us get rid of nested selectors. All selectors in a BEM project have the same weight. That means it is much easier to redefine styles written according to BEM. Now, to use the same form in another project, you can just copy its layout and styles.

The idea of the naming of BEM components is that you can explicitly define the connection between the block and its elements.

Modifiers And Mixes

Officially, “M” stands for Modifier, but it also implies one more important notion in BEM: “mix”. Both modifiers and mixes make changes to a block and its elements. Let’s take a closer look at this.

Modifiers

A modifier defines the look, state and behavior of a block or an element. Adding modifiers is optional. Modifiers let you combine different block features, as you can use any number of modifiers. But a block or an element can’t be assigned different values of the same modifier.

Let’s explore how modifiers work.

Imagine the project needs the same search form as in the example above. It should have the same functions but look different (for example, the search forms in the header and in the footer of the page should differ). The first thing you can do to change the appearance of the form is to write additional styles:

header .form {}
footer .form {}

The header .form selector has more weight than the form selector, which means that one rule will override the other one. But as we have discussed, nested selectors increase code coupling and make reuse difficult, so this approach doesn’t work for us.

In BEM, you can use a modifier to add new styles to the block:

<!-- Added the form_type_original modifier-->
<form class="form form_type_original" action="/">
  <input class="form__search" name="s">
  <input class="form__submit" type="submit">
</form>

The line <form class="form form_type_original"></form> indicates that the block was assigned a type modifier with the original value. In a classic scheme, the modifier name is separated from the block or element name with an underscore.

The form can have a unique color, size, type, or design theme. All these parameters can be set with a modifier:

<form class="form form_type_original form_size_m form_theme_forest">

The same form can look different but stay the same size:

<form class="form form_type_original form_size_m form_theme_forest"></form>
<form class="form form_type_original form_size_m form_theme_sun"></form>

But the selectors for each modifier will still have the same weight:

.form_type_original {}
.form_size_m {}
.form_theme_forest {}

Important: A modifier contains only additional styles that change the original block implementation in some way. This allows you to set the appearance of a universal block only once, and add only those features that differ from the original block code into the modifier styles.

.form 
  /* universal block styles */

.form_type_original 
  /* added styles */

This is why a modifier should always be on the same DOM node with the block and the element it is associated with.

<form class="form form_type_original"></form>

You can use modifiers to apply universal components in very specific cases. The block and element code doesn’t change. The necessary combination of modifiers is created on the DOM node.

Mixes

A mix allows you to apply the same formatting to different HTML elements and combine the behavior and styles of several entities while avoiding code duplication. They can replace abstract wrapper blocks.

A mix means that you host several BEM entities (blocks, elements, modifiers) on a single DOM node. Similar to modifiers, mixes are used for changing blocks. Let’s look at some examples of when you should use a mix.

Blocks can differ not only visually but also semantically. For example, a search form, a registration form and a form for ordering cakes are all forms. In the layout, they are implemented with the “form” block but they don’t have any styles in common. It is impossible to handle such differences with a modifier.
You can define common styles for such blocks but you won’t be able to reuse the code.

.form,
.search,
.register 
  ...

You can use a mix to create semantically different blocks for the same form:

<form class="form" action="/">
  <input class="form__search" name="s">
  <input class="form__submit" type="submit">
</form>

The .form class selector describes all styles that can be applied to any form (order, search or registration):

.form {}

Now you can make a search form from the universal form. To do this, create an additional search class in the project. This class will be responsible only for the search. To combine the styles and behavior of the.formand.search classes, place these classes on a single DOM node:


<form class="form search" action="/">
  <input class="form__search" name="s">
  <input class="form__submit" type="submit">
</form>

In this case, the .search class is a separate block that defines behavior. This block can’t have modifiers responsible for the form, themes, and sizes. These modifiers already belong to the universal form. A mix helps to combine the styles and behavior of these blocks.

Let’s take one more example where the component’s semantics is changed. Here is a navigation menu in the page header in which all entries are links:

<nav class="menu">
  <a class="link" href=""></a>
  <a class="link" href=""></a>
  <a class="link" href=""></a>
</nav>

The link functionality is already implemented in the link block, but the menu links have to differ visually from the links in the text. There are several ways to change the menu links:

  1. Create a menu entry modifier that turns the entry into a link:
    <nav class="menu">
      <a class="menu__item menu__item_link" href=""></a>
      <a class="menu__item menu__item_link" href=""></a>
      <a class="menu__item menu__item_link" href=""></a>
    </nav>
    

    In this case, to implement the modifier, you should copy the `link` block behavior and styles. This will lead to code duplication.

  2. Use a mix of the `link` universal block and the `item` element of the `menu` block:
    <nav class="menu">
      <a class="link menu__item" href=""></a>
      <a class="link menu__item" href=""></a>
      <a class="link menu__item" href=""></a>
    </nav>
    

    With the mix of the two BEM entities, you can now implement the basic link functionality from the `link` block and additional CSS rules from the `menu` block, and avoid code duplication.

External Geometry And Positioning: Giving Up Abstract HTML Wrappers

Mixes are used to position a block relative to other blocks or to position elements inside a block. In BEM, styles responsible for geometry and positioning are set in the parent block. Let’s take a universal menu block that has to be placed in the header. In the layout, the block has to have a 20px indent from the parent block.

This task has several solutions:

  1. Write styles with indents for the menu block:
    .menu 
      margin-left: 20px;
    
    

    In this case, the “menu” block isn’t universal anymore. If you have to place the menu in the page footer, you will have to edit styles because the indents will probably be different.

  2. Create the menu block modifier:
    <div>
      <ul class="menu menu_type_header">
        <li class="menu__item"><a href=""></a></li>
        <li class="menu__item"><a href=""></a></li>
        <li class="menu__item"><a href=""></a></li>
      </ul>
    </div>
    
    .menu_type_header 
      margin-left: 20px;
    
    .menu_type_footer 
      margin-left: 30px;
    
    

    In this case, the project will include two kinds of menus, although this is not the case. The menu stays the same.

  3. Define the external positioning of the block:  nest the `menu` block in the abstract wrapper (for example, the `wrap` block) setting all indents:
    <div class="wrap">
      <ul class="menu">
        <li class="menu__item"><a href=""></a></li>
        <li class="menu__item"><a href=""></a></li>
        <li class="menu__item"><a href=""></a></li>
      </ul>
    </div>
    

    To avoid the temptation to create modifiers and change the block styles to position the block on the page, you need to understand one thing:

    The indent from a parent block isn’t a feature of the nested block. It is a feature of the parent block. It has to know that the nested block has to be indented from the border by a certain number of pixels.

  4. Use a mix. The information about nested block positioning is included in the parent block elements. Then the parent block element is mixed into the nested block. In this case, the nested block doesn’t specify any indents and can be easily reused in any place.

Let’s go on with our example:

<div>
  <ul class="menu header__menu">
    <li class="menu__item"><a href=""></a></li>
    <li class="menu__item"><a href=""></a></li>
    <li class="menu__item"><a href=""></a></li>
  </ul>
</div>

In this case, external geometry and positioning of the menu block are set through the header__menu element. The menu block doesn’t specify any indents and can be easily reused.

The parent block element (in our case it is header__menu) performs the task of the wrapper blocks responsible for external positioning of the block.

Blocks In The File Structure

All BEM projects have a similar file structure. The familiar file structure makes it easier for developers to navigate the project, switch between projects, and move blocks from one project to another.

The implementation of each block is stored in a separate project folder. Each technology (CSS, JavaScript, tests, templates, documentation, images) is in a separate file.

For example, if the input block appearance is set with CSS, the code is saved in the input.css file.

project
  common.blocks/
    input/
      input.css # The "input" block implementation with CSS
      input.js  # The "input" block implementation with JavaScript

The code for modifiers and elements is also stored in separate files of the block. This approach allows you to include in the build only those modifiers and elements that are necessary for the implementation of the block.

project
  common.blocks/
    input/
      input.css           # The "input" block implementation with CSS
      input.js            # The "input" block implementation with JavaScript
      input_theme_sun.css # The "input_theme_sun" modifier implementation
      input__clear.css    # The "input__clear" element implementation with CSS
      input__clear.js     # The "input__clear" element implementation with JavaScript

To improve the project navigation, combine block modifiers with multiple values in directories.

The file structure of any BEM project consists of redefinition levels (you can learn more on them over here). Redefinition levels allow you to:

  • Divide the project into platforms;
  • Easily update the block libraries included in the project;
  • Use common blocks to develop multiple projects;
  • Change the design themes without affecting the project logic;
  • Conduct experiments in a live project.

Using blocks and storing all block technologies in the same folder makes it easy to move blocks between projects. To move all styles and behavior of the block together with the layout, just copy the block folder to the new project.

Non-Evident Advantages Of The Methodology

The Convenience Of Parallel Development

In BEM, any layout is divided into blocks. Because the blocks are independent, they can be developed in parallel by several developers.

A developer creates a block as a universal component that can be reused in any other project.

An example is the bem-components block library, which contains universal blocks, such as a link, button, and input field. It is easier to create more complex blocks from universal components. For example, a selector or checkbox.

Using blocks in project layout helps you save the time on integrating code written by several developers, guarantees the uniqueness of component names, and lets you test blocks at the development stage.

Testing The Layout

It is problematic to test the functionality of the whole page, especially in a dynamic project connected to a database.

In BEM, each block is covered by tests. Tests are a block implementation technology, like Javascript or CSS. Blocks are tested at the development stage. It is easier to check the correctness of one block and then assemble the project from tested blocks. After that, all you have to do is to make sure that the block wrapper is working correctly.

Customizable Build Of A Project

For convenient development, all blocks and technologies in a BEM project are placed in separate folders and files. To combine the source files into a single file (for example, to put all CSS files in project.css, all JS files in project.js, and so on), we use the build process.

The build performs the following tasks:

  • Combines source files that are spread out across the project’s file system;
  • Includes only necessary blocks, elements, and modifiers (BEM entities) in the project;
  • Follows the order for including entities;
  • Processes the source file code during the build (e.g. compiles LESS code to CSS code).

To include only the necessary BEM entities in the build, you need to create a list of blocks, elements, and modifiers used on the pages. This list is called a declaration.

Since BEM blocks are developed independently and placed in separate files in the file system, they don’t ‘know’ anything about each other. To build blocks based on other blocks, specify dependencies. There is a BEM technology responsible for this: the deps.js files. Dependency files let the build engine know which additional blocks have to be included in the project.

Practical Case: BEM Is Not Only For CSS

In the previous sections, all code examples are for CSS. But BEM allows you to modify the behavior of the block and its representation in HTML in the same declarative way like in CSS.

How To Use Templating In BEM

In HTML, the block markup is repeated every time the block appears on the page. If you create the HTML markup manually and then need to fix an error or make changes, you will need to modify the markup for every instance of the block. To generate HTML code and apply fixes automatically, BEM uses templates; blocks are responsible for the way they are presented in HTML.

Templates allow you to:

  • Reduce the time used for project debugging, because the template changes are automatically applied to all project blocks;
  • Modify the block layout;
  • Move blocks with the current layout to another project.

BEM uses the bem-xjst template engine which features two engines:

  • BEMHTML
    Transforms the BEMJSON description of the page to HTML. The templates are described in .bemhtml.js files.
  • BEMTREE
    Transforms data to BEMJSON. The templates are described in BEMJSON format in .bemtree.js files.

If templates aren’t written for the blocks, the template engine sets the <div> tag for the blocks by default.

Compare the declaration of the blocks and the HTML output:

Declaration:


  block: 'menu',
  content: [
    
      elem: 'item',
      content: 
        block: 'link'
    },
     
      elem: 'item',
      elemMods:  current: true , // Set the modifier for the menu item
      content: 
        block: 'link'
      
    }
  ]
}

HTML:

<div class="menu">
  <div class="menu__item">
    <div class="link"></div>
  </div>
  <div class="menu__item menu__item_current">
    <div class="link"></div>
  </div>
</div>

To modify the menu block layout, you need to write templates for the block:

  1. Let’s change the menu block tag:
    block('menu')(
      tag()('menu') // Set the "menu" tag for the menu block 
    )
    

    Modified HTML:

    <menu class="menu"> <!-- Replace the "div" tag with the "menu" tag for the "menu" block -->
      <div class="menu__item">
        <div class="link"></div>
      </div>
      <div class="menu__item menu__item_current">
        <div class="link"></div>
      </div>
    </menu>
    

    Similar to CSS, the template is applied to all “menu” blocks on the page.

  2. Add an extra element (menu__inner) that works as an inner wrapper and is responsible for the layout of the elements in the menu block. Originally the menu__inner element wasn’t included in the declaration, so we have to add it when the templates are built.

    BEM templates are written in JavaScript, so you can also use JavaScript to add a new element to the template:

    block('menu')(
      tag()('menu'),
      content()(function() 
        return 
          elem: 'inner',
          content: this.ctx.content
        ;
      })
    )
    
    <menu class="menu"> <!-- Replace the "div" tag with the "menu" tag for the "menu" block -->
      <div class="menu__inner">
        <div class="menu__item">
          <div class="link"></div>
        </div>
        <div class="menu__item menu__item_current">
          <div class="link"></div>
        </div>
      </div>
    </menu>
    
  3. Replace tags for all inner and item elements:
    block('menu')(
      tag()('menu'),
      content()(function() 
        return 
          elem: 'inner',
          content: this.ctx.content
        
      }),
      elem('inner')(
        tag()('ul')
      ),
      elem('item')(
        tag()('li')
      )
    )
    
    <menu class="menu">
      <ul class="menu__inner">
        <li class="menu__item">
          <div class="link"></div>
        </li>
        <li class="menu__item menu__item_current">
          <div class="link"></div>
        </li>
      </ul>
    </menu>
    
  4. Set the <a> tag for all links on the page:
    block('menu')(
      tag()('menu'),
      content()(function() 
        return 
          elem: 'inner',
          content: this.ctx.content
        
      }),
      elem('inner')(
        tag()('ul')
      ),
      elem('item')(
        tag()('li')
      )
    );
    block('link')(
      tag()('a')
    );
    
    <menu class="menu">
      <ul class="menu__inner">
        <li class="menu__item">
          <a class="link"></a>
        </li>
        <li class="menu__item menu__item_current">
          <a class="link"></a>
        </li>
      </ul>
    </menu>
    
  5. Modify the existing template. Rules in templates are applied in the same way as in CSS: a lower rule overrides a higher rule. Add new rules to the template, and change the link tag from <a> to <span>:
    block('link')(
      tag()('a')
    );
    block('link')(
      tag()('span')
    );
    
    <menu class="menu">
      <ul class="menu__inner">
        <li class="menu__item">
          <span class="link"></span>
        </li>
        <li class="menu__item menu__item_current">
          <span class="link"></span>
        </li>
      </ul>
    </menu>
    

BEM Is A Customizable System

BEM methodology provides you strict rules to create a system in your project. But at the same time, a lot of BEM rules can be customized. BEM methodology allows you to change the naming convention, choose the most convenient file structure or add any technologies you want to the block.

Now you can tune in the system and make your own superhero of BEM!


BEM as a Captain America logo


BEM Captain America

How To Get More Out Of BEM

To start learning BEM principles, visit our website. If you have any questions you’d like to ask the team, join our Telegram channel or open up a discussion in our BEM Forum.

Smashing Editorial
(rb, ra, il)


Continue reading here: 

BEM For Beginners: Why You Need BEM

Thumbnail

How To Deliver A Successful UX Project In The Healthcare Sector




How To Deliver A Successful UX Project In The Healthcare Sector

Sven Jungmann & Karolin Neubauer



A mid-career UX researcher was hired to understand the everyday needs, perceptions, and concerns of patients in a hospital in Berlin, Germany. She used rigorous observation and interviewing methods just like she teaches them to design thinking students at a nearby university. She returned with a handful of actionable insights that our product team found useful, somewhat at least.

However, we were surprised that her recommendations gravitated towards convenience issues such as “Patients want to know the food menu” or “Users struggle to remember who their doctors are.” Entirely missing were reports of physical and psychological complaints. We would at least have expected sleeping problems: Given that 80% of working Germans don’t sleep well and nearly 10% even appear to have severe sleeping disorders (link in German), why did no one mention it?

“We only see what we know.”

— Johann Wolfgang von Goethe (1749-1832)

If you are a UX researcher about to embark on a project with hospitalized patients and you want to avoid missing out on deep concerns and problems of users, then maybe this article can help you strengthen your awareness for particular challenges of clinical UX.

It is difficult to get in touch with real patients and get permission from clinical staff to access the right people. We are fortunate to have access to a network of more than 100 hospitals in Germany thanks to our sister Helios Kliniken GmbH, which is Europe’s largest private hospital provider. Our experience with clinical UX research taught us the importance of stressing that we cannot assume that patients bring up relevant concerns by themselves.

We describe three reasons we think are important to improve the quality and quantity of your findings. Generally speaking, this article emphasizes the need for UX practitioners to be mindful of their participants’ emotional and physical state. But we also discuss how we think UX researchers should prepare for and conduct a research project in the healthcare sector.

The Three B’s That Complicate UX Research In Hospitals

We’ve been thinking much about user research in hospital settings recently. Our new company, smart Helios, a digital health development firm, is a spin-off of Helios Kliniken, Europe’s largest private hospital chain. To inform our lean software development, we thoroughly embrace iterative empathetic observation and end-user interviews at each step of a development cycle (i.e., ideation, prototyping, and testing).

We learned that qualitative research in a hospital setting brings its challenges. We think it is worth considering them, especially those we discuss here, called the three B’s: Biases, Barriers, and Background.

Here’s an overview:

  1. Biases
    Psychological mechanisms can affect our patients’ thinking and hence diminish the results of our findings.
  2. Barriers to trust
    Patients rarely share intimate needs with a non-medical interviewer easily. This can create blind spots in our research.
  3. Background
    Internal and external factors such as wealth, socio-economic status, sanitation, education, and access to healthcare can influence our health as well as the care patients receive. The quality of care also depends on the hospital’s infrastructure and staff experience with a particular disease or procedure. These differences make it challenging to generalize findings.

We also discuss remedies that can help overcome these challenges and distill more valuable insights. They include:

  • Conducting a thorough and well-prepared interview.
  • Including healthcare providers in the process.
  • Drawing on quantitative data to guide some of the qualitative user research.

Biases: We Are Fantastic At Lying To Ourselves, Whether Or Not We’re Patients

Psychologists call them cognitive biases: they negatively affect how accurately we perceive and remember events or feelings, and we possess impressive amounts of them.

For example, people remember information better if it is more recent or salient. If you ask a patient about the initial appointment with her oncologist (this is rarely good news), don’t be surprised if she remembers only a quarter of it. Hospitalized patients are typically overwhelmed by the unfamiliar situation they’re in and often under stress and that influences their memory.

Hence it makes a difference when you ask them in their patient journey. Even in one day, patients face different problems that affect what’s top of mind at the moment you observe or interview them:

  • In the morning, when the painkillers have worn off during sleep, regaining control over physical pain is all that matters.
  • During the day, worries about upcoming procedures might dominate their thoughts or they could be focused on their hunger while they’re not allowed to eat or drink ahead of a diagnostic test.
  • In the evening, they might be afraid that they won’t be able to update their relatives appropriately.
  • At night, some struggle to sleep because of the hospital noise or their worries.

Takeaways:

  • Try interviewing users at different times of the day and different moments of their journey and take note of how findings vary.
  • Always get an orientation about where your users stand within their patient journey. Explore what happened in the previous hours or days and what diagnostics or treatments lie ahead of them.
  • Beware that our psyches possess a plethora of mechanisms to limit rationality and prevent past events from entering our conscious mind. You cannot control them all, but it improves your research if you notice them.

Barriers To Trust: Who Is Asking Matters, Too

It’s not just about how and when; it also matters who is asking. Even if a patient agrees to speak with us, what she shares will highly depend on how much she trusts us. One of us observed as a clinician how often his patients need to build up trust, sometimes over days until they ‘confess’ certain concerns. That’s especially true when problems have a psychological component (e.g., sleeping disorders) or are stigmatized (e.g., certain infectious diseases).

The more knowledgeable you are about health problems, the better you can steer interviews towards relevant issues. If you do this empathetically, your interviewees might find it easier to speak about them. Don’t get frustrated, however, if they don’t. Some people need a lot of trust, and there’s rarely a shortcut to earning it. In these cases, there’s something else you can do: include subjects who are not your target users but still have crucial insights in your interviews.

Take the nurse, for example. She might know from her previous night shift how many patients had trouble sleeping and who might agree to talk about it. The doctors will know which crucial questions they get asked frequently. And the housekeeping staff can share stories about the patients’ hygiene concerns. Listen closely to them: many of the staffs’ pain points likely hint to patients’ pain points, too. The more observers you allow to shed light on one subject, the better your chances to understand your patients’ experiences and the broader your perception of the system will be.

Takeaways:

  • Try to interview people involved in your users’ care.
  • Ask the clinical staff for guidance on which patients to ask about specific problems.
  • Even if patients are your primary users, make sure to ask health care providers, such as doctors, nurses, or therapists about common patient needs.
  • Schedule repeated interviews with patients if possible to build the trust necessary for sharing critical concerns.

The ‘living’ patient journey map.


A large, printed and ‘living’ patient journey helps to constantly challenge and refine assumptions. (Large preview)

Background: Mind The Worlds In And Around The Patients

Different patients have different needs. This is obvious, and part of the reason why UX researchers develop personas, conduct semi-structured interviews, focus group discussions, and observe subjects to explore the multiple realities of our participants. But there’s still a problem we can’t dismiss. Even within our hospitals inside Germany, people’s realities can differ greatly depending on their income, education, insurance, place of residence, etc.

What you discover in one hospital or region might not apply elsewhere. This can be a problem if you want to deploy your products at scale. If you have the opportunity, you should go the extra mile to conduct UX research in different hospitals to understand the needs and what drives adoption.

If some regions show poor sales, conduct field research in these regions to revisit your personas. In fact, don’t just challenge the personas, also explore the environment.

Here’s an example of why that matters: We developed a tool that relies on data from a hospital information system. This worked well in one clinic were staff was spread over different parts of the building, turning digital communication into an effective medium. In another hospital, however, the relevant people sat in the same room, making face-to-face communication significantly better than typing information into electronic records.

Takeaways:

  • Go beyond personas or archetypes and seek to understand the different realities between hospitals, wards, and regions.
  • Develop and constantly improve a rollout-playbook that lists local challenges and how you solved them to inform future expansions.

Sorry, Ignorance Is Not Bliss

Some UX researchers seem to believe that it is beneficial to enter an interview unprepared to avoid bias. Some shy away from acquiring relevant medical knowledge, thinking that (since they are not trained medical professionals) they won’t grasp the concepts anyway. Some feel that understanding the medical context of their interviewees’ situation will not add value to their research since they solely focus on the subjective experience of the disease.

But in evidence-based healthcare, conducting research on patients without previous peer-reviewed literature and guideline research is not only unprofessional but often even considered unethical. We should not fall prey to the illusion that ignorance frees us from bias.

The good news is that in healthcare, we are privileged to have a large body of well-conducted studies and systematic reviews available online, many of them free to access. PubMed is an excellent and open source, tutorials on how to use are abundantly available online (we think this is a good primer). Or if you have the budget, paid sites like UpToDate provide comprehensive disease reviews written both for professionals and for laypeople.

We know that UX researchers who are rightfully focused on ‘getting out of the building’ might not enjoy spending many hours on literature research but we are convinced that this will help you form better hypotheses and questions.

Moreover, if you start with clearly predefined research questions and seek answers in the scientific medical literature, you might save time and discover questions that you wouldn’t have thought of. For example, it is advised that individuals undergoing hip surgery should practice using crutches before the operation because it is already difficult enough even without the postoperative pain and swelling. This knowledge, obtained from literature research, could help move from more open questions, such as:

  • “How do patients prepare for a hip replacement surgery?” or
  • “What perceived needs to patients have ahead of hip surgery?”

To more concrete questions such as:

  • “What are the most important preparatory measures that many patients are currently unaware of?”

Takeaway:

  • Do a systematic literature review to inform your research.

Let The Data Guide You And You Guide The Data

To take this further, we’re also developing methods to use quantitative analytics and Deep Learning to guide our qualitative research. Our machine learning engineer just deployed AI to crawl the web for colon cancer blogs to identify hot topics that remained unmentioned in qualitative reviews. We defined “hot” as having many views, many comments, and many likes.

Or you can uncover semantic structures (see picture). These findings can then guide the UX researchers. Similarly, qualitative research can yield hypotheses that we can try to validate with passively collected data. For example, if you think that sleeping problems are common, you could (user consent provided) use your app to measure phone use at night as a proxy for sleeplessness.


Informing our researchers about hot topics in colon cancer using machine learning (topic modeling, credits Yuki Katoh and Ellen Hoeven)


Deploying topic modeling to find semantic structures in texts on colon cancer to inform our qualitative researchers. (Large preview)

Conclusion

Many of our suggestions are not new to well-trained UX researchers. We are aware of that. But in our experience, it is worth stressing the importance of mindfulness towards the three Bs: Biases, Barriers to trust, and Background. Here’s a summary of some of the recommendations to overcome the 3 Bs:

  • Prepare interviews with literature research on the topic (e.g., on Pubmed.gov).
  • Ask doctors which patients are suitable for interview.
  • Include those who care for your users, including nurses, therapists, and relatives.
  • Cooperate with the data scientists or web analysts in your team, if you have them.
  • Understand that it takes users time to build trust to tell you about some needs.
  • Explore how realities differ not only by personas, but also by regions and hospitals.
  • Stay aware that, no matter how much you try, the influence of the 3Bs can only be reduced, and not entirely removed.

We wish you well and thank you for making the world a healthier place.


The authors would like to thank their former colleague, Tim Leinert, for his thoughtful input to this piece.

Smashing Editorial
(cc, ra, yk, il)


See original article here: 

How To Deliver A Successful UX Project In The Healthcare Sector

Thumbnail

Lessons Learned While Developing WordPress Plugins




Lessons Learned While Developing WordPress Plugins

Jakub Mikita



Every WordPress plugin developer struggles with tough problems and code that’s difficult to maintain. We spend late nights supporting our users and tear out our hair when an upgrade breaks our plugin. Let me show you how to make it easier.

In this article, I’ll share my five years of experience developing WordPress plugins. The first plugin I wrote was a simple marketing plugin. It displayed a call to action (CTA) button with Google’s search phrase. Since then, I’ve written another 11 free plugins, and I maintain almost all of them. I’ve written around 40 plugins for my clients, from really small ones to one that have been maintained for over a year now.

Measuring Performance With Heatmaps

Heatmaps can show you the exact spots that receive the most engagement on a given page. Find out why they’re so efficient for your marketing goals and how they can be integrated with your WordPress site. Read article →

Good development and support lead to more downloads. More downloads mean more money and a better reputation. This article will show you the lessons I’ve learned and the mistakes I’ve made, so that you can improve your plugin development.

1. Solve A Problem

If your plugin doesn’t solve a problem, it won’t get downloaded. It’s as simple as that.

Take the Advanced Cron Manager plugin (8,000+ active installations). It helps WordPress users who are having a hard time debugging their cron. The plugin was written out of a need — I needed something to help myself. I didn’t need to market this one, because people already needed it. It scratched their itch.

On the other hand, there’s the Bug — fly on the screen plugin (70+ active installations). It randomly simulates a fly on the screen. It doesn’t really solve a problem, so it’s not going to have a huge audience. It was a fun plugin to develop, though.

Focus on a problem. When people don’t see their SEO performing well, they install an SEO plugin. When people want to speed up their website, they install a caching plugin. When people can’t find a solution to their problem, then they find a developer who writes a solution for them.

As David Hehenberger attests in his article about writing a successful plugin, need is a key factor in the WordPress user’s decision of whether to install a particular plugin.

If you have an opportunity to solve someone’s problem, take a chance.

2. Support Your Product

“3 out of 5 Americans would try a new brand or company for a better service experience. 7 out of 10 said they were willing to spend more with companies they believe provide excellent service.”

— Nykki Yeager

Don’t neglect your support. Don’t treat it like a must, but more like an opportunity.

Good-quality support is critical in order for your plugin to grow. Even a plugin with the best code will get some support tickets. The more people who use your plugin, the more tickets you’ll get. A better user experience will get you fewer tickets, but you will never reach inbox 0.

Every time someone posts a message in a support forum, I get an email notification immediately, and I respond as soon as I can. It pays off. The vast majority of my good reviews were earned because of the support. This is a side effect: Good support often translates to 5-star reviews.

When you provide excellent support, people start to trust you and your product. And a plugin is a product, even if it’s completely free and open-source.

Good support is more complex than about writing a short answer once a day. When your plugin gains traction, you’ll get several tickets per day. It’s a lot easier to manage if you’re proactive and answer customers’ questions before they even ask.

Here’s a list of some actions you can take:

  • Create an FAQ section in your repository.
  • Pin the “Before you ask” thread at the top of your support forum, highlighting the troubleshooting tips and FAQ.
  • Make sure your plugin is simple to use and that users know what they should do after they install it. UX is important.
  • Analyze the support questions and fix the pain points. Set up a board where people can vote for the features they want.
  • Create a video showing how the plugin works, and add it to your plugin’s main page in the WordPress.org repository.

It doesn’t really matter what software you use to support your product. The WordPress.org’s official support forum works just as well as email or your own support system. I use WordPress.org’s forum for the free plugins and my own system for the premium plugins.

3. Don’t Use Composer

Composer is package-manager software. A repository of packages is hosted on packagist.org, and you can easily download them to your project. It’s like NPM or Bower for PHP. Managing your third-party packages the way Composer does is a good practice, but don’t use it in your WordPress project.

I know, I dropped a bomb. Let me explain.

Composer is great software. I use it myself, but not in public WordPress projects. The problem lies in conflicts. WordPress doesn’t have any global package manager, so each and every plugin has to load dependencies of their own. When two plugins load the same dependency, it causes a fatal error.

There isn’t really an ideal solution to this problem, but Composer makes it worse. You can bundle the dependency in your source manually and always check whether you are safe to load it.

Composer’s issue with WordPress plugins is still not solved, and there won’t be any viable solution to this problem in the near future. The problem was raised many years ago, and, as you can read in WP Tavern’s article, many developers are trying to solve it, without any luck.

The best you can do is to make sure that the conditions and environment are good to run your code.

4. Reasonably Support Old PHP Versions

Don’t support very old versions of PHP, like 5.2. The security issues and maintenance aren’t worth it, and you’re not going to earn more installations from those older versions.


The Notification plugin’s usage on PHP versions from May 2018. (Large preview)

Go with PHP 5.6 as a minimal requirement, even though official support will be dropped by the end of 2018. WordPress itself requires PHP 7.2.

There’s a movement that discourages support of legacy PHP versions. The Yoast team released the Whip library, which you can include in your plugin and which displays to your users important information about their PHP version and why they should upgrade.

Tell your users which versions you do support, and make sure their website doesn’t break after your plugin is installed on too low a version.

5. Focus On Quality Code

Writing good code is tough in the beginning. It takes time to learn the “SOLID” principles and design patterns and to change old coding habits.

It once took me three days to display a simple string in WordPress, when I decided to rewrite one of my plugins using better coding practices. It was frustrating knowing that it should have taken 30 minutes. Switching my mindset was painful but worth it.

Why was it so hard? Because you start writing code that seems at first to be overkill and not very intuitive. I kept asking myself, “Is this really needed?” For example, you have to separate the logic into different classes and make sure each is responsible for a single thing. You also have to separate classes for the translation, custom post type registration, assets management, form handlers, etc. Then, you compose the bigger structures out of the simple small objects. That’s called dependency injection. That’s very different from having “front end” and “admin” classes, where you cram all your code.

The other counterintuitive practice was to keep all actions and filters outside of the constructor method. This way, you’re not invoking any actions while creating the objects, which is very helpful for unit testing. You also have better control over which methods are executed and when. I wish I knew this before I wrote a project with an infinite loop caused by the actions in the constructor methods. Those kinds of bugs are hard to trace and hard to fix. The project had to be refactored.

The above are but a few examples, but you should get to know the SOLID principles. These are valid for any system and any coding language.

When you follow all of the best practices, you reach the point where every new feature just fits in. You don’t have to tweak anything or make any exceptions to the existing code. It’s amazing. Instead of getting more complex, your code just gets more advanced, without losing flexibility.

Also, format your code properly, and make sure every member of your team follows a standard. Standards will make your code predictable and easier to read and test. WordPress has its own standards, which you can implement in your projects.

6. Test Your Plugin Ahead Of Time

I learned this lesson the hard way. Lack of testing led me to release a new version of a plugin with a fatal error. Twice. Both times, I got a 1-star rating, which I couldn’t turn into a positive review.

You can test manually or automatically. Travis CI is a continuous testing product that integrates with GitHub. I’ve built a really simple test suite for my Notification plugin that just checks whether the plugin can boot properly on every PHP version. This way, I can be sure the plugin is error-free, and I don’t have to pay much attention to testing it in every environment.

Each automated test takes a fraction of a second. 100 automated tests will take about 10 minutes to complete, whereas manual testing needs about 2 minutes for each case.

The more time you invest in testing your plugin up front, the more it will save you in the long run.

To get started with automated testing, you can use the WP-CLI \`wp scaffold plugin-test\` command, which installs all of the configuration you need.

7. Document Your Work

It’s a cliche that developers don’t like to write documentation. It’s the most boring part of the development process, but a little goes a long way.

Write self-documenting code. Pay attention to variable, function and class names. Don’t make any complicated structures, like cascades that can’t be read easily.

Another way to document code is to use the “doc block”, which is a comment for every file, function and class. If you write how the function works and what it does, it will be so much easier to understand when you need to debug it six months from now. WordPress Coding Standards covers this part by forcing you to write the doc blocks.

Using both techniques will save you the time of writing the documentation, but the code documentation is not going to be read by everyone.

For the end user, you have to write high-quality, short and easy-to-read articles explaining how the system works and how to use it. Videos are even better; many people prefer to watch a short tutorial than read an article. They are not going to look at the code, so make their lives easier. Good documentation also reduces support tickets.

Conclusion

These seven rules have helped me develop good-quality products, which are starting to be a core business at BracketSpace. I hope they’ll help you in your journey with WordPress plugins as well.

Let me know in the comments what your golden development rule is or whether you’ve found any of the above particularly helpful.

Smashing Editorial
(il, ra, yk)


Link:  

Lessons Learned While Developing WordPress Plugins

Thumbnail

Building Mobile Apps Using React Native And WordPress




Building Mobile Apps Using React Native And WordPress

Muhammad Muhsin



As web developers, you might have thought that mobile app development calls for a fresh learning curve with another programming language. Perhaps Java and Swift need to be added to your skill set to hit the ground running with both iOS and Android, and that might bog you down.

But this article has you in for a surprise! We will look at building an e-commerce application for iOS and Android using the WooCommerce platform as our backend. This would be an ideal starting point for anyone willing to get into native cross-platform development.

A Brief History Of Cross-Platform Development

It’s 2011, and we see the beginning of hybrid mobile app development. Frameworks like Apache Cordova, PhoneGap, and Ionic Framework slowly emerge. Everything looks good, and web developers are eagerly coding away mobile apps with their existing knowledge.

However, mobile apps still looked like mobile versions of websites. No native designs like Android’s material design or iOS’s flat look. Navigation worked similar to the web and transitions were not buttery smooth. Users were not satisfied with apps built using the hybrid approach and dreamt of the native experience.

Fast forward to March 2015, and React Native appears on the scene. Developers are able to build truly native cross-platform applications using React, a favorite JavaScript library for many developers. They are now easily able to learn a small library on top of what they know with JavaScript. With this knowledge, developers are now targeting the web, iOS and Android.

Furthermore, changes done to the code during development are loaded onto the testing devices almost instantly! This used to take several minutes when we had native development through other approaches. Developers are able to enjoy the instant feedback they used to love with web development.

React developers are more than happy to be able to use existing patterns they have followed into a new platform altogether. In fact, they are targeting two more platforms with what they already know very well.

This is all good for front-end development. But what choices do we have for back-end technology? Do we still have to learn a new language or framework?

The WordPress REST API

In late 2016, WordPress released the much awaited REST API to its core, and opened the doors for solutions with decoupled backends.

So, if you already have a WordPress and WooCommerce website and wish to retain exactly the same offerings and user profiles across your website and native app, this article is for you!

Assumptions Made In This Article

I will walk you through using your WordPress skill to build a mobile app with a WooCommerce store using React Native. The article assumes:

  • You are familiar with the different WordPress APIs, at least at a beginner level.
  • You are familiar with the basics of React.
  • You have a WordPress development server ready. I use Ubuntu with Apache.
  • You have an Android or an iOS device to test with Expo.

What We Will Build In This Tutorial

The project we are going to build through this article is a fashion store app. The app will have the following functionalities:

  • Shop page listing all products,
  • Single product page with details of the selected item,
  • ‘Add to cart’ feature,
  • ‘Show items in cart’ feature,
  • ‘Remove item from cart’ feature.

This article aims to inspire you to use this project as a starting point to build complex mobile apps using React Native.

Note: For the full application, you can visit my project on Github and clone it.

Getting Started With Our Project

We will begin building the app as per the official React Native documentation. Having installed Node on your development environment, open up the command prompt and type in the following command to install the Create React Native App globally.

npm install -g create-react-native-app

Next, we can create our project

create-react-native-app react-native-woocommerce-store

This will create a new React Native project which we can test with Expo.

Next, we will need to install the Expo app on our mobile device which we want to test. It is available for both iOS and Android.

On having installed the Expo app, we can run npm start on our development machine.

cd react-native-woocommerce-store

npm start


Starting a React Native project through the command line via Expo. (Large preview)

After that, you can scan the QR code through the Expo app or enter the given URL in the app’s search bar. This will run the basic ‘Hello World’ app in the mobile. We can now edit App.js to make instant changes to the app running on the phone.

Alternatively, you can run the app on an emulator. But for brevity and accuracy, we will cover running it on an actual device.

Next, let’s install all the required packages for the app using this command:

npm install -s axios react-native-htmlview react-navigation react-redux redux redux-thunk

Setting Up A WordPress Site

Since this article is about creating a React Native app, we will not go into details about creating a WordPress site. Please refer to this article on how to install WordPress on Ubuntu. As WooCommerce REST API requires HTTPS, please make sure it is set up using Let’s Encrypt. Please refer to this article for a how-to guide.

We are not creating a WordPress installation on localhost since we will be running the app on a mobile device, and also since HTTPS is needed.

Once WordPress and HTTPS are successfully set up, we can install the WooCommerce plugin on the site.


Installing the WooCommerce plugin to our WordPress installation. (Large preview)

After installing and activating the plugin, continue with the WooCommerce store setup by following the wizard. After the wizard is complete, click on ‘Return to dashboard.’

You will be greeted by another prompt.


Adding example products to WooCommerce. (Large preview)

Click on ‘Let’s go‘ to ‘Add example products’. This will save us the time to create our own products to display in the app.

Constants File

To load our store’s products from the WooCommerce REST API, we need the relevant keys in place inside our app. For this purpose, we can have a constans.js file.

First create a folder called ‘src’ and create subfolders inside as follows:


Create the file ‘Constants.js’ within the constans folder. (Large preview)

Now, let’s generate the keys for WooCommerce. In the WordPress dashboard, navigate to WooCommerce → Settings → API → Keys/Apps and click on ‘Add Key.’

Next create a Read Only key with name React Native. Copy over the Consumer Key and Consumer Secret to the constants.js file as follows:

const Constants = 
   URL: 
wc: 'https://woocommerce-store.on-its-way.com/wp-json/wc/v2/'
   ,
   Keys: 
ConsumerKey: 'CONSUMER_KEY_HERE',
ConsumerSecret: 'CONSUMER_SECRET_HERE'
   
}
export default Constants;

Starting With React Navigation

React Navigation is a community solution to navigating between the different screens and is a standalone library. It allows developers to set up the screens of the React Native app with just a few lines of code.

There are different navigation methods within React Navigation:

  • Stack,
  • Switch,
  • Tabs,
  • Drawer,
  • and more.

For our Application we will use a combination of StackNavigation and DrawerNavigation to navigate between the different screens. StackNavigation is similar to how browser history works on the web. We are using this since it provides an interface for the header and the header navigation icons. It has push and pop similar to stacks in data structures. Push means we add a new screen to the top of the Navigation Stack. Pop removes a screen from the stack.

The code shows that the StackNavigation, in fact, houses the DrawerNavigation within itself. It also takes properties for the header style and header buttons. We are placing the navigation drawer button to the left and the shopping cart button to the right. The drawer button switches the drawer on and off whereas the cart button takes the user to the shopping cart screen.

const StackNavigation = StackNavigator(
 DrawerNavigation:  screen: DrawerNavigation 
}, 
   headerMode: 'float',
   navigationOptions: ( navigation, screenProps ) => (
     headerStyle:  backgroundColor: '#4C3E54' ,
     headerTintColor: 'white',
     headerLeft: drawerButton(navigation),
     headerRight: cartButton(navigation, screenProps)
   })
 });

const drawerButton = (navigation) => (
 <Text
   style= padding: 15, color: 'white' }
   onPress=() => 
     if (navigation.state.index === 0) 
       navigation.navigate('DrawerOpen')
      else 
       navigation.navigate('DrawerClose')
     
   }
   }> (
 <Text style= padding: 15, color: 'white' }
   onPress=() =>  navigation.navigate('CartPage') }
 >
   <EvilIcons name="cart" size=30 />
   screenProps.cartCount
 </Text>
);

DrawerNavigation on the other hands provides for the side drawer which will allow us to navigate between Home, Shop, and Cart. The DrawerNavigator lists the different screens that the user can visit, namely Home page, Products page, Product page, and Cart page. It also has a property which will take the Drawer container: the sliding menu which opens up when clicking the hamburger menu.

const DrawerNavigation = DrawerNavigator(
 Home: 
   screen: HomePage,
   navigationOptions: 
     title: "RN WC Store"
   
 },
 Products: 
   screen: Products,
   navigationOptions: 
     title: "Shop"
   
 },
 Product: 
   screen: Product,
   navigationOptions: ( navigation ) => (
     title: navigation.state.params.product.name
   ),
 },
 CartPage: 
   screen: CartPage,
   navigationOptions: 
     title: "Cart"
   
 }
}, 
   contentComponent: DrawerContainer
 );

#


Left: The Home page (homepage.js). Right: The open drawer (DrawerContainer.js).

Injecting The Redux Store To App.js

Since we are using Redux in this app, we have to inject the store into our app. We do this with the help of the Provider component.

const store = configureStore();

class App extends React.Component 
 render() 
   return (
     <Provider store=store>    
       <ConnectedApp />    
     </Provider>    
   )
 }
}

We will then have a ConnectedApp component so that we can have the cart count in the header.

class CA extends React.Component 
 render() 
   const cart = 
     cartCount: this.props.cart.length
   
   return (
     <StackNavigation screenProps=cart />
   );
 }
}

function mapStateToProps(state) 
 return 
   cart: state.cart
 ;
}

const ConnectedApp = connect(mapStateToProps, null)(CA);

Redux Store, Actions, And Reducers

In Redux, we have three different parts:

  1. Store
    Holds the whole state of your entire application. The only way to change state is to dispatch an action to it.
  2. Actions
    A plain object that represents an intention to change the state.
  3. Reducers
    A function that accepts a state and an action type and returns a new state.

These three components of Redux help us achieve a predictable state for the entire app. For simplicity, we will look at how the products are fetched and saved in the Redux store.

First of all, let’s look at the code for creating the store:

let middleware = [thunk];

export default function configureStore() 
    return createStore(
        RootReducer,
        applyMiddleware(...middleware)
    );

Next, the products action is responsible for fetching the products from the remote website.

export function getProducts() 
   return (dispatch) => 
       const url = `$Constants.URL.wcproducts?per_page=100&consumer_key=$Constants.Keys.ConsumerKey&consumer_secret=$Constants.Keys.ConsumerSecret`
      
       return axios.get(url).then(response => 
           dispatch(
               type: types.GET_PRODUCTS_SUCCESS,
               products: response.data
           
       )}).catch(err => 
           console.log(err.error);
       )
   };
}

The products reducer is responsible for returning the payload of data and whether it needs to be modified.

export default function (state = InitialState.products, action) 
    switch (action.type) 
        case types.GET_PRODUCTS_SUCCESS:
            return action.products;
        default:
            return state;
    
}

Displaying The WooCommerce Shop

The products.js file is our Shop page. It basically displays the list of products from WooCommerce.

class ProductsList extends Component 

 componentDidMount() 
   this.props.ProductAction.getProducts(); 
 

 _keyExtractor = (item, index) => item.id;

 render() 
   const  navigate  = this.props.navigation;
   const Items = (
     <FlatList contentContainerStyle=styles.list numColumns=2
       data=this.props.products  
       keyExtractor=this._keyExtractor
       renderItem=
         ( item ) => (
           <TouchableHighlight style= width: '50%' } onPress=() => navigate("Product",  product: item )} underlayColor="white">
             <View style=styles.view >
               <Image style=styles.image source= uri: item.images[0].src } />
               <Text style=styles.text>item.name</Text>
             </View>
           </TouchableHighlight>
         )
       }
     />
   );
   return (
     <ScrollView>
       this.props.products.length ? Items :
         <View style= alignItems: 'center', justifyContent: 'center' }>
           <Image style=styles.loader source=LoadingAnimation />
         </View>
       }
     </ScrollView>
   );
 }
}

this.props.ProductAction.getProducts() and this.props.products are possible because of mapStateToProps and mapDispatchToProps.


Products listing screen. (Large preview)

mapStateToProps and mapDispatchToProps

State is the Redux store and Dispatch is the actions we fire. Both of these will be exposed as props in the component.

function mapStateToProps(state) 
 return 
   products: state.products
 ;
}
function mapDispatchToProps(dispatch) 
 return 
   ProductAction: bindActionCreators(ProductAction, dispatch)
 ;
}
export default connect(mapStateToProps, mapDispatchToProps)(ProductsList);

Styles

In React, Native styles are generally defined on the same page. It’s similar to CSS, but we use camelCase properties instead of hyphenated properties.

const styles = StyleSheet.create(
 list: 
   flexDirection: 'column'
 ,
 view: 
   padding: 10
 ,
 loader: 
   width: 200,
   height: 200,
   alignItems: 'center',
   justifyContent: 'center',
 ,
 image: 
   width: 150,
   height: 150
 ,
 text: 
   textAlign: 'center',
   fontSize: 20,
   padding: 5
 
});

Single Product Page

This page contains details of a selected product. It shows the user the name, price, and description of the product. It also has the ‘Add to cart’ function.


Single product page. (Large preview)

Cart Page

This screen shows the list of items in the cart. The action has the functions getCart, addToCart, and removeFromCart. The reducer handles the actions likewise. Identification of actions is done through actionTypes — constants which describe the action that are stored in a separate file.

export const GET_PRODUCTS_SUCCESS = 'GET_PRODUCTS_SUCCESS'
export const GET_PRODUCTS_FAILED = 'GET_PRODUCTS_FAILED';

export const GET_CART_SUCCESS = 'GET_CART_SUCCESS';
export const ADD_TO_CART_SUCCESS = 'ADD_TO_CART_SUCCESS';
export const REMOVE_FROM_CART_SUCCESS = 'REMOVE_FROM_CART_SUCCESS';

This is the code for the CartPage component:

class CartPage extends React.Component 

 componentDidMount() 
   this.props.CartAction.getCart();
 

 _keyExtractor = (item, index) => item.id;

 removeItem(item) 
   this.props.CartAction.removeFromCart(item);
 

 render() 
   const  cart  = this.props;
   console.log('render cart', cart)

   if (cart && cart.length > 0) {
     const Items = <FlatList contentContainerStyle=styles.list
       data=cart
       keyExtractor=this._keyExtractor
       renderItem=( item ) =>
         <View style=styles.lineItem >
           <Image style=styles.image source= uri: item.image } />
           <Text style=styles.text>item.name</Text>
           <Text style=styles.text>item.quantity</Text>
           <TouchableOpacity style= marginLeft: 'auto' } onPress=() => this.removeItem(item)><Entypo name="cross" size=30 /></TouchableOpacity>
         </View>
       }
     />;
     return (
       <View style=styles.container>
         Items
       </View>
     )
   } else {
     return (
       <View style=styles.container>
         <Text>Cart is empty!</Text>
       </View>
     )
   }
 }
}

As you can see, we are using a FlatList to iterate through the cart items. It takes in an array and creates a list of items to be displayed on the screen.


#


Left: The cart page when it has items in it. Right: The cart page when it is empty.

Conclusion

You can configure information about the app such as name and icon in the app.json file. The app can be published after npm installing exp.

To sum up:

  • We now have a decent e-commerce application with React Native;
  • Expo can be used to run the project on a smartphone;
  • Existing backend technologies such as WordPress can be used;
  • Redux can be used for managing the state of the entire app;
  • Web developers, especially React developers can leverage this knowledge to build bigger apps.

For the full application, you can visit my project on Github and clone it. Feel free to fork it and improve it further. As an exercise, you can continue building more features into the project such as:

  • Checkout page,
  • Authentication,
  • Storing the cart data in AsyncStorage so that closing the app does not clear the cart.
Smashing Editorial
(da, lf, ra, yk, il)


More here: 

Building Mobile Apps Using React Native And WordPress

Thumbnail

Contributing To MDN Web Docs




Contributing To MDN Web Docs

Rachel Andrew



MDN Web Docs has been documenting the web platform for over twelve years and is now a cross-platform effort with contributions and an Advisory Board with members from Google, Microsoft and Samsung as well as those representing Firefox. Something that is fundamental to MDN is that it is a huge community effort, with the web community helping to create and maintain the documentation. In this article, I’m going to give you some pointers as to the places where you can help contribute to MDN and exactly how to do so.

If you haven’t contributed to an open source project before, MDN is a brilliant place to start. Skills needed range from copyediting, translating from English to other languages, HTML and CSS skills for creating Interactive Examples, or an interest in browser compatibility for updating Browser Compatibility data. What you don’t need to do is to write a whole lot of code to contribute. It’s very straightforward, and an excellent way to give back to the community if you have ever found these docs useful.

Contributing To The Documentation Pages

The first place you might want to contribute is to the MDN docs themselves. MDN is a wiki, so you can log in and start to help by correcting or adding to any of the documentation for CSS, HTML, JavaScript or any of the other parts of the web platform covered by MDN.

To start editing, you need to log in using GitHub. As is usual with a wiki, any editors of a page are listed, and this section will use your GitHub username. If you look at any of the pages on MDN contributors are listed at the bottom of the page, the below image shows the current contributors to the page on CSS Grid Layout.


A list showing names of people who contributed to this page


The contributors to the CSS Grid Layout page. (Large preview)

What Might You Edit?

Things that you might consider as an editor are fixing obvious typos and grammatical errors. If you are a good proofreader and copyeditor, then you may well be able to improve the readability of the docs by fixing any spelling or other errors that you spot.

You might also spot a technical error, or somewhere the specs have changed and where an update or clarification would be useful. With the huge range of web platform features covered by MDN and the rate of change, it is very easy for things to get out of date, if you spot something – fix it!

You may be able to use some specific knowledge you have to add additional information. For example, Eric Bailey has been adding Accessibility Concerns sections to many pages. This is a brilliant effort to highlight the things we should be thinking about when using a certain thing.


A screenshot of the Accessibility Concerns section


This section highlights the things we should be aware of when using background-color. (Large preview)

Another place you could add to a page is in adding “See also” links. These could be links to other parts of MDN, or to external resources. When adding external resources, these should be highly relevant to the property, element or technique being described by that document. A good candidate would be a tutorial which demonstrates how to use that feature, something which would give a reader searching for information a valuable next step.

How To Edit A Document?

Once you are logged in you will see a link to Edit on pages in MDN, clicking this will take you into a WYSIWYG editor for editing content. Your first few edits are likely to be small changes, in which case you should be able to follow your nose and edit the text. If you are making extensive edits, then it would be worth taking a look at the style guide first. There is also a guide to using the WYSIWYG Editor.

After making your edit, you can Preview and then Publish. Before publishing it is a good idea to explain what you added and why using the Revision Comment field.


Screenshot of this field in the edit form


Add a comment using the Revision Comment field. (Large preview)

Language Translations

Those of us with English as a first language are incredibly fortunate when it comes to information on the web, being able to get pretty much all of the information that we could ever want in our own language. If you are able to translate English language pages into other languages, then you can help to translate MDN Web Docs, making all of this information available to more people.


A screenshot showing the drop-down translations list


Translations available for the background-color page. (Large preview)

If you click on the language icon on any page, you can see which languages that information has been translated into, and you can add your own translations following the information on the page Translating MDN Pages.

Interactive Examples

The Interactive Examples on MDN, are the examples that you will see at the top of many pages of MDN, such as this one for the grid-area property.


Screenshot of an Interactive Example


The Interactive Example for the grid-area property. (Large preview)

These examples allow visitors to MDN to try out various values for CSS properties or try out a JavaScript function, right there on MDN without needing to head into a development environment to do so. The project to add these examples has been in progress for around a year, you can read about the project and progress to date in the post Bringing Interactive Examples to MDN.

The content for these Interactive Examples is held in the Interactive Examples GitHub repository. For example, if you wanted to locate the example for grid-area, you would find it in that repo under live-examples/css-examples/grid. Under that folder, you will find two files for grid-area, an HTML and a CSS file.

grid-area.html


<section id="example-choice-list" class="example-choice-list large" data-property="grid-area">
    <div class="example-choice" initial-choice="true">
        <pre><code class="language-css">grid-area: a;</code></pre>
        <button type="button" class="copy hidden" aria-hidden="true">
        <span class="visually-hidden">Copy to Clipboard</span>
        </button>
    </div>
    
    <div class="example-choice">
        <pre><code class="language-css">grid-area: b;</code></pre>
        <button type="button" class="copy hidden" aria-hidden="true">
        <span class="visually-hidden">Copy to Clipboard</span>
        </button>
    </div>
    
    <div class="example-choice">
        <pre><code class="language-css">grid-area: c;</code></pre>
        <button type="button" class="copy hidden" aria-hidden="true">
        <span class="visually-hidden">Copy to Clipboard</span>
        </button>
    </div> 
    
    <div class="example-choice">
        <pre><code class="language-css">grid-area: 2 / 1 / 2 / 4;</code></pre>
        <button type="button" class="copy hidden" aria-hidden="true">
        <span class="visually-hidden">Copy to Clipboard</span>
        </button>
    </div> 
</section>
    
<div id="output" class="output large hidden">
    <section id="default-example" class="default-example">
        <div class="example-container">
            <div id="example-element" class="transition-all">Example</div>
        </div>
    </section>
</div>

grid.area.css


.example-container 
    background-color: #eee;
    border: .75em solid;
    padding: .75em;
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    grid-template-rows: repeat(3, minmax(40px, auto));
    grid-template-areas: 
    "a a a"
    "b c c"
    "b c c";
    grid-gap: 10px;
    width: 200px;
    
    
    .example-container > div 
    background-color: rgba(0, 0, 255, 0.2);
    border: 3px solid blue;
    
    
    example-element 
    background-color: rgba(255, 0, 200, 0.2);
    border: 3px solid rebeccapurple;
    

An Interactive Example is just a small demo, which uses some standard classes and IDs in order that the framework can pick up the example and make it interactive, where the values can be changed by a visitor to the page who wants to quickly see how it works. To add or edit an Interactive Example, first fork the Interactive Examples repo, clone it to your machine and follow the instructions on the Contributing page to install the required packages from npm and be able to build and test examples locally.

Then create a branch and edit or create your new example. Once you are happy with it, send a Pull Request to the Interactive Examples repo to ask for your example to be reviewed. In order to keep the examples consistent, reviews are fairly nitpicky but should point out the changes you need to make in a clear way, so you can update your example and have it approved, merged and added to an MDN page.


Screenshot of a tweet asking for help with HTML examples


MDN looking for help with HTML Interactive Examples. (Large preview)

With pretty much all of CSS now covered (in addition to the JavaScript examples), MDN is now looking for help to build the HTML examples. There are instructions as to how to get started in a post on the MDN Discourse Forum. Check out that post as it gives links to a Google doc that you can use to indicate that you are working on a particular example, as well as some other useful information.

The Interactive Examples are incredibly useful for people exploring the web platform, so adding to the project is an excellent way to contribute. Contributing to CSS or HTML examples requires knowledge of CSS and HTML, plus the ability to think up a clear demonstration. This last point is often the hardest part, I’ve created a lot of CSS Interactive Examples and spent more time thinking up the best example for each property than I do actually writing the code.

Browser Compat Data

Fairly recently the browser compatibility data listed on MDN Pages has begun to be updated through the Browser Compatibility Project. This project is developing browser compat data in JSON format, which can display the compatibility tables on MDN but also be useful data for other purposes.


An example screenshot of the old tables on MDN


The Old Browser Compat Tables on MDN. (Large preview)


An example screenshot of the new tables on MDN


The New Browser Compat Tables on MDN. (Large preview)

The Browser Compatibility Data is on GitHub, and if you find a page that has incorrect information or is still using the old tables, you can submit a Pull Request. The repository contains contribution information, however, the simplest way to start is to edit an existing example. I recently updated the information for the CSS shape-outside property. The property already had some data in the new format, but it was incomplete and incorrect.

To edit this data, I first forked the Browser Compat Data so that I had my own fork. I then cloned that to my machine and created a new branch to make my changes in.

Once I had my new branch, I found the JSON file for shape-outside and was able to make my edits. I already had a good idea about browser support for the property; I also used the live example on the shape-outside MDN page to test to see support when I wasn’t sure. Therefore making the edits was a case of working through the file, checking the version numbers listed for support of the property and updating those which were incorrect.

As the file is in JSON format is pretty straightforward to edit in any text editor. The .editorconfig file explains the simple formatting rules for these documents. There are also some helpful tips in this checklist.

Once you have made your edits, you can commit your changes, push your branch to your fork and then make a Pull Request to the Browser Compat Data repository. It’s likely that, as with the live examples, the reviewer will have some changes for you to make. In my PR for the Shapes data I had a few errors in how I had flagged the data and needed to make some changes to links. These were simple to make, and then my PR was merged.

Get Started

You can get started simply by picking something to add to and starting work on it in many cases. If you have any questions or need some help with any of this, then the MDN Discourse forum is a good place to post. MDN is the place I go to look up information, the place I send new developers and experienced developers alike, and its strength is the fact that we can all work to make it better.

If you have never made a Pull Request on a project before, it is a very friendly place to make that first PR and, as I hope I have shown, there are ways to contribute that don’t require writing any code at all. A very valuable skill for any documentation project is that of writing, editing and translating as these skills can help to make technical documentation easier to read and accessible to more people around the world.

Smashing Editorial
(il)


Read this article:

Contributing To MDN Web Docs

Thumbnail

Building A Serverless Contact Form For Your Static Site




Building A Serverless Contact Form For Your Static Site

Brian Holt



Static site generators provide a fast and simple alternative to Content Management Systems (CMS) like WordPress. There’s no server or database setup, just a build process and simple HTML, CSS, and JavaScript. Unfortunately, without a server, it’s easy to hit their limits quickly. For instance, in adding a contact form.

With the rise of serverless architecture adding a contact form to your static site doesn’t need to be the reason to switch to a CMS anymore. It’s possible to get the best of both worlds: a static site with a serverless back-end for the contact form (that you don’t need to maintain). Maybe best of all, in low-traffic sites, like portfolios, the high limits of many serverless providers make these services completely free!

In this article, you’ll learn the basics of Amazon Web Services (AWS) Lambda and Simple Email Service (SES) APIs to build your own static site mailer on the Serverless Framework. The full service will take form data submitted from an AJAX request, hit the Lambda endpoint, parse the data to build the SES parameters, send the email address, and return a response for our users. I’ll guide you through getting Serverless set up for the first time through deployment. It should take under an hour to complete, so let’s get started!

The static site form, sending the message to the Lambda endpoint and returning a response to the user.
The static site form, sending the message to the Lambda endpoint and returning a response to the user.

Setting Up

There are minimal prerequisites in getting started with Serverless technology. For our purposes, it’s simply a Node Environment with Yarn, the Serverless Framework, and an AWS account.

Setting Up The Project


The Serverless Framework web site. Useful for installation and documentation.


The Serverless Framework web site. Useful for installation and documentation.

We use Yarn to install the Serverless Framework to a local directory.

  1. Create a new directory to host the project.
  2. Navigate to the directory in your command line interface.
  3. Run yarn init to create a package.json file for this project.
  4. Run yarn add serverless to install the framework locally.
  5. Run yarn serverless create --template aws-nodejs --name static-site-mailer to create a Node service template and name it static-site-mailer.

Our project is setup but we won’t be able to do anything until we set up our AWS services.

Setting Up An Amazon Web Services Account, Credentials, And Simple Email Service


The Amazon Web Services sign up page, which includes a generous free tier, enabling our project to be entirely free.


The Amazon Web Services sign up page, which includes a generous free tier, enabling our project to be entirely free.

The Serverless Framework has recorded a video walk-through for setting up AWS credentials, but I’ve listed the steps here as well.

  1. Sign Up for an AWS account or log in if you already have one.
  2. In the AWS search bar, search for “IAM”.
  3. On the IAM page, click on “Users” on the sidebar, then the “Add user” button.
  4. On the Add user page, give the user a name – something like “serverless” is appropriate. Check “Programmatic access” under Access type then click next.
  5. On the permissions screen, click on the “Attach existing policies directly” tab, search for “AdministratorAccess” in the list, check it, and click next.
  6. On the review screen you should see your user name, with “Programmatic access”, and “AdministratorAccess”, then create the user.
  7. The confirmation screen shows the user “Access key ID” and “Secret access key”, you’ll need these to provide the Serverless Framework with access. In your CLI, type yarn sls config credentials --provider aws --key YOUR_ACCESS_KEY_ID --secret YOUR_SECRET_ACCESS_KEY, replacing YOUR_ACCESS_KEY_ID and YOUR_SECRET_ACCESS_KEY with the keys on the confirmation screen.

Your credentials are configured now, but while we’re in the AWS console let’s set up Simple Email Service.

  1. Click Console Home in the top left corner to go home.
  2. On the home page, in the AWS search bar, search for “Simple Email Service”.
  3. On the SES Home page, click on “Email Addresses” in the sidebar.
  4. On the Email Addresses listing page, click the “Verify a New Email Address” button.
  5. In the dialog window, type your email address then click “Verify This Email Address”.
  6. You’ll receive an email in moments containing a link to verify the address. Click on the link to complete the process.

Now that our accounts are made, let’s take a peek at the Serverless template files.

Setting Up The Serverless Framework

Running serverless create creates two files: handler.js which contains the Lambda function, and serverless.yml which is the configuration file for the entire Serverless Architecture. Within the configuration file, you can specify as many handlers as you’d like, and each one will map to a new function that can interact with other functions. In this project, we’ll only create a single handler, but in a full Serverless Architecture, you’d have several of the various functions of the service.


The default file structure generated from the Serverless Framework containing handler.js and serverless.yml.


The default file structure generated from the Serverless Framework containing handler.js and serverless.yml.

In handler.js, you’ll see a single exported function named hello. This is currently the main (and only) function. It, along with all Node handlers, take three parameters:

  • event
    This can be thought of as the input data for the function.
  • context object
    This contains the runtime information of the Lambda function.
  • callback
    An optional parameter to return information to the caller.
// handler.js

'use strict';

module.exports.hello = (event, context, callback) => 
  const response = 
    statusCode: 200,
    body: JSON.stringify(
      message: 'Go Serverless v1.0! Your function executed successfully!',
      input: event,
    ),
  };

  callback(null, response);
};

At the bottom of hello, there’s a callback. It’s an optional argument to return a response, but if it’s not explicitly called, it will implicitly return with null. The callback takes two parameters:

  • Error error
    For providing error information for when the Lambda itself fails. When the Lambda succeeds, null should be passed into this parameter.
  • Object result
    For providing a response object. It must be JSON.stringify compatible. If there’s a parameter in the error field, this field is ignored.

Our static site will send our form data in the event body and the callback will return a response for our user to see.

In serverless.yml you’ll see the name of the service, provider information, and the functions.

# serverless.yml

service: static-site-mailer

provider:
  name: aws
  runtime: nodejs6.10

functions:
  hello:
    handler: handler.hello

How the function names in serverless.yml map to handler.js.


How the function names in serverless.yml map to handler.js.

Notice the mapping between the hello function and the handler? We can name our file and function anything and as long as it maps to the configuration it will work. Let’s rename our function to staticSiteMailer.

# serverless.yml

functions:
  staticSiteMailer:
    handler: handler.staticSiteMailer
// handler.js

module.exports.staticSiteMailer = (event, context, callback) => 
  ...
;

Lambda functions need permission to interact with other AWS infrastructure. Before we can send an email, we need to allow SES to do so. In serverless.yml, under provider.iamRoleStatements add the permission.

# serverless.yml

provider:
  name: aws
  runtime: nodejs6.10
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "ses:SendEmail"
      Resource: ["*"]

Since we need a URL for our form action, we need to add HTTP events to our function. In serverless.yml we create a path, specify the method as post, and set CORS to true for security.

functions:
  staticSiteMailer:
    handler: handler.staticSiteMailer
    events:
      - http:
          method: post
          path: static-site-mailer
          cors: true

Our updated serverless.yml and handler.js files should look like:

# serverless.yml

service: static-site-mailer

provider:
  name: aws
  runtime: nodejs6.10

functions:
  staticSiteMailer:
    handler: handler.staticSiteMailer
    events:
      - http:
          method: post
          path: static-site-mailer
          cors: true

provider:
  name: aws
  runtime: nodejs6.10
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "ses:SendEmail"
      Resource: ["*"]
// handler.js

'use strict';

module.exports.staticSiteMailer = (event, context, callback) => 
  const response = 
    statusCode: 200,
    body: JSON.stringify(
      message: 'Go Serverless v1.0! Your function executed successfully!',
      input: event,
    ),
  };

  callback(null, response);
};

Our Serverless Architecture is setup, so let’s deploy it and test it. You’ll get a simple JSON response.

yarn sls deploy --verbose
yarn sls invoke --function staticSiteMailer


    "statusCode": 200,
    "body": ""message":"Go Serverless v1.0! Your function executed successfully!","input":}"
}

The return response from invoking our brand new serverless function.


The return response from invoking our brand new serverless function.

Creating The HTML Form

Our Lambda function input and form output need to match, so before we build the function we’ll build the form and capture its output. We keep it simple with name, email, and message fields. We’ll add the form action once we’ve deployed our serverless architecture and got our URL, but we know it will be a POST request so we can add that in. At the end of the form, we add a paragraph tag for displaying response messages to the user which we’ll update on the submission callback.

<form action=" SERVICE URL }" method="POST">
  <label>
    Name
    <input type="text" name="name" required>
  </label>
  <label>
    Email
    <input type="email" name="reply_to" required>
  </label>
  <label>
    Message:
    <textarea name="message" required></textarea>
  </label>
  <button type="submit">Send Message</button>
</form>
<p id="js-form-response"></p>

To capture the output we add a submit handler to the form, turn our form parameters into an object, and send stringified JSON to our Lambda function. In the Lambda function we use JSON.parse() to read our data. Alternatively, you could use jQuery’s Serialize or query-string to send and parse the form parameters as a query string but JSON.stringify() and JSON.parse() are native.

(() => 
  const form = document.querySelector('form');
  const formResponse = document.querySelector('js-form-response');

  form.onsubmit = e => 
    e.preventDefault();

    // Prepare data to send
    const data = ;
    const formElements = Array.from(form);
    formElements.map(input => (data[input.name] = input.value));

    // Log what our lambda function will receive
    console.log(JSON.stringify(data));
  };
})();

Go ahead and submit your form then capture the console output. We’ll use it in our Lambda function next.


Capturing the form data in a console log.


Capturing the form data in a console log.

Invoking Lambda Functions

Especially during development, we need to test our function does what we expect. The Serverless Framework provides the invoke and invoke local command to trigger your function from live and development environments respectively. Both commands require the function name passed through, in our case staticSiteMailer.

yarn sls invoke local --function staticSiteMailer

To pass mock data into our function, create a new file named data.json with the captured console output under a body key within a JSON object. It should look something like:

// data.json


  "body": ""name": "Sender Name","reply_to": "sender@email.com","message": "Sender message""
}

To invoke the function with the local data, pass the --path argument along with the path to the file.

yarn sls invoke local --function staticSiteMailer --path data.json

An updated return response from our serverless function when we pass it JSON data.


An updated return response from our serverless function when we pass it JSON data.

You’ll see a similar response to before, but the input key will contain the event we mocked. Let’s use our mock data to send an email using Simple Email Service!

Sending An Email With Simple Email Service

We’re going to replace the staticSiteMailer function with a call to a private sendEmail function. For now you can comment out or remove the template code and replace it with:

// hander.js

function sendEmail(formData, callback) 
  // Build the SES parameters
  // Send the email


module.exports.staticSiteMailer = (event, context, callback) => 
  const formData = JSON.parse(event.body);

  sendEmail(formData, function(err, data) 
    if (err) 
      console.log(err, err.stack);
     else 
      console.log(data);
    
  });
};

First, we parse the event.body to capture the form data, then we pass it to a private sendEmail function. sendEmail is responsible for sending the email, and the callback function will return a failure or success response with err or data. In our case, we can simply log the error or data since we’ll be replacing this with the Lambda callback in a moment.

Amazon provides a convenient SDK, aws-sdk, for connecting their services with Lambda functions. Many of their services, including SES, are part of it. We add it to the project with yarn add aws-sdk and import it into the top the handler file.

// handler.js

const AWS = require('aws-sdk');
const SES = new AWS.SES();

In our private sendEmail function, we build the SES.sendEmail parameters from the parsed form data and use the callback to return a response to the caller. The parameters require the following as an object:

  • Source
    The email address SES is sending from.
  • ReplyToAddresses
    An array of email addresses added to the reply to the field in the email.
  • Destination
    An object that must contain at least one ToAddresses, CcAddresses, or BccAddresses. Each field takes an array of email addresses that correspond to the to, cc, and bcc fields respectively.
  • Message
    An object which contains the Body and Subject.

Since formData is an object we can call our form fields directly like formData.message, build our parameters, and send it. We pass your SES-verified email to Source and Destination.ToAddresses. As long as the email is verified you can pass anything here, including different email addresses. We pluck our reply_to, message, and name off our formData object to fill in the ReplyToAddresses and Message.Body.Text.Data fields.

// handler.js
function sendEmail(formData, callback) 
  const emailParams = 
    Source: 'your_email@example.com', // SES SENDING EMAIL
    ReplyToAddresses: [formData.reply_to],
    Destination: 
      ToAddresses: ['your_email@example.com'], // SES RECEIVING EMAIL
    ,
    Message: 
      Body: 
        Text: 
          Charset: 'UTF-8',
          Data: `$formData.messagennName: $formData.namenEmail: $formData.reply_to`,
        },
      },
      Subject: 
        Charset: 'UTF-8',
        Data: 'New message from your_site.com',
      ,
    },
  };

  SES.sendEmail(emailParams, callback);
}

SES.sendEmail will send the email and our callback will return a response. Invoking the local function will send an email to your verified address.

yarn sls invoke local --function testMailer --path data.json

The return response from SES.sendEmail when it succeeds.


The return response from SES.sendEmail when it succeeds.

Returning A Response From The Handler

Our function sends an email using the command line, but that’s not how our users will interact with it. We need to return a response to our AJAX form submission. If it fails, we should return an appropriate statusCode as well as the err.message. When it succeeds, the 200 statusCode is sufficient, but we’ll return the mailer response in the body as well. In staticSiteMailer we build our response data and replace our sendEmail callback function with the Lambda callback.

// handler.js

module.exports.staticSiteMailer = (event, context, callback) => 
  const formData = JSON.parse(event.body);

  sendEmail(formData, function(err, data) 
    const response = 
      statusCode: err ? 500 : 200,
      headers: 
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': 'https://your-domain.com',
      ,
      body: JSON.stringify(
        message: err ? err.message : data,
      ),
    };

    callback(null, response);
  });
};

Our Lambda callback now returns both success and failure messages from SES.sendEmail. We build the response with checks if err is present so our response is consistent. The Lambda callback function itself passes null in the error argument field and the response as the second. We want to pass errors onwards, but if the Lambda itself fails, its callback will be implicitly called with the error response.

In the headers, you’ll need to replace Access-Control-Allow-Origin with your own domain. This will prevent any other domains from using your service and potentially racking up an AWS bill in your name! And I don’t cover it in this article, but it’s possible to set-up Lambda to use your own domain. You’ll need to have an SSL/TLS certificate uploaded to Amazon. The Serverless Framework team wrote a fantastic tutorial on how to do so.

Invoking the local function will now send an email and return the appropriate response.

yarn sls invoke local --function testMailer --path data.json

The return response from our serverless function, containing the SES.sendEmail return response in the body.


The return response from our serverless function, containing the SES.sendEmail return response in the body.

Calling The Lambda Function From The Form

Our service is complete! To deploy it run yarn sls deploy -v. Once it’s deployed you’ll get a URL that looks something like https://r4nd0mh45h.execute-api.us-east-1.amazonaws.com/dev/static-site-mailer which you can add to the form action. Next, we create the AJAX request and return the response to the user.

(() => 
  const form = document.querySelector('form');
  const formResponse = document.querySelector('js-form-response');

  form.onsubmit = e => 
    e.preventDefault();

    // Prepare data to send
    const data = ;
    const formElements = Array.from(form);
    formElements.map(input => (data[input.name] = input.value));

    // Log what our lambda function will receive
    console.log(JSON.stringify(data));

    // Construct an HTTP request
    var xhr = new XMLHttpRequest();
    xhr.open(form.method, form.action, true);
    xhr.setRequestHeader('Accept', 'application/json; charset=utf-8');
    xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');

    // Send the collected data as JSON
    xhr.send(JSON.stringify(data));

    // Callback function
    xhr.onloadend = response => 
      if (response.target.status === 200) 
        // The form submission was successful
        form.reset();
        formResponse.innerHTML = 'Thanks for the message. I’ll be in touch shortly.';
       else 
        // The form submission failed
        formResponse.innerHTML = 'Something went wrong';
        console.error(JSON.parse(response.target.response).message);
      
    };
  };
})();

In the AJAX callback, we check the status code with response.target.status. If it’s anything other than 200 we can show an error message to the user, otherwise let them know the message was sent. Since our Lambda returns stringified JSON we can parse the body message with JSON.parse(response.target.response).message. It’s especially useful to log the error.

You should be able to submit your form entirely from your static site!

The static site form, sending the message to the Lambda endpoint and returning a response to the user.
The static site form, sending the message to the Lambda endpoint and returning a response to the user.

Next Steps

Adding a contact form to your static is easy with the Serverless Framework and AWS. There’s room for improvement in our code, like adding form validation with a honeypot, preventing AJAX calls for invalid forms and improving the UX if the response, but this is enough to get started. You can see some of these improvements within the static site mailer repo I’ve created. I hope I’ve inspired you to try out Serverless yourself!

Smashing Editorial
(lf, ra, il)


See original article here:

Building A Serverless Contact Form For Your Static Site

Thumbnail

Working Together: How Designers And Developers Can Communicate To Create Better Projects




Working Together: How Designers And Developers Can Communicate To Create Better Projects

Rachel Andrew



Among the most popular suggestions on Smashing Magazine’s Content User Suggestions board is the need of learning more about the interaction and communication between designers and developers. There are probably several articles worth of very specific things that could be covered here, but I thought I would kick things off with a general post rounding up some experiences on the subject.

Given the wide range of skills held by the line-up at our upcoming SmashingConf Toronto — a fully live, no-slides-allowed event, I decided to solicit some feedback. I’ve wrapped those up with my own experience of 20 years working alongside designers and other developers. I hope you will add your own experiences in the comments.

Some tips work best when you can be in the same room as your team, and others are helpful for the remote worker or freelancer. What shines through all of the advice, however, is the need to respect each other, and the fact that everyone is working to try and create the best outcome for the project.

Working Remotely And Staying Connected

The nomadic lifestyle is not right for everyone, but the only way to know for sure is to try. If you can afford to take the risk, go for it. Javier Cuello shares his experience and insights from his four years of travel and work. Read article →

For many years, my own web development company operated as an outsourced web development provider for design agencies. This involved doing everything from front-end development to implementing e-commerce and custom content management solutions. Our direct client was the designer or design agency who had brought us on board to help with the development aspect of the work, however, in an ideal situation, we would be part of the team working to deliver a great end result to the end client.

Sometimes this relationship worked well. We would feel a valued part of the team, our ideas and experience would count, we would work with the designers to come up with the best solution within budgetary, time, and other constraints.

In many cases, however, no attempt was made to form a team. The design agency would throw a picture of a website as a PDF file over the fence to us, then move on to work on their next project. There was little room for collaboration, and often the designer who had created the files was busy on some other work when we came back with questions.

It was an unsatisfactory way to work for everyone. We would be frustrated because we did not have a chance to help ensure that what was designed was possible to be built in a performant and accessible way, within the time and budget agreed on. The designer of the project would be frustrated: Why were these developers asking so many questions? Can they not just build the website as I have designed? Why are the fonts not the size I wanted?

The Waterfall versus Agile argument might be raised here. The situation where a PDF is thrown over the fence is often cited as an example of how bad a Waterfall approach is. Still, working in a fully Agile way is often not possible for teams made of freelancers or separate parties doing different parts of the work. Therefore, in reading these suggestions, look at them through the lens of the projects you work on. However, try not to completely discount something as unworkable because you can’t use the full process. There are often things we can take without needing to fully adopt one methodology or another.

Setting Up A Project For Success

I came to realize that very often the success of failure of the collaboration started before we even won the project, with the way in which we proposed the working relationship. We had to explain upfront that experience had taught us that the approach of us being handed a PDF, quoting and returning a website did not give the best results.

Projects that were successful had a far more iterative approach. It might not be possible to have us work alongside the designers or in a more Agile way. However, having a number of rounds of design and development with time for feedback from each side went a long way to prevent the frustrations of a method where work was completed by each side independently.

Creating Working Relationships

Having longer-term relationships with an agency, spanning a number of projects worked well. We got to know the designers, learned how they worked, could anticipate their questions and ensure that we answered them upfront. We were able to share development knowledge, the things that made a design easier or harder to implement which would, therefore, have an impact on time and budget. They were able to communicate better with us in order to explain why a certain design element was vital, even if it was going to add complexity.

For many freelance designers and developers, and also for those people who work for a distributed company, communication can become mostly text-based. This can make it particularly hard to build relationships. There might be a lot of communication — by email, in Slack, or through messages on a project management platform such as Basecamp. However, all of these methods leave us without the visual cues we might pick up from in-person meetings. An email we see as to the point may come across to the reader as if we are angry. The quick-fire nature of tools such as Slack might leave us committing in writing something which we would not say to that person while looking into their eyes!

Freelance data scientist Nadieh Bremer will talk to us about visualizing data in Toronto. She has learned that meeting people face to face — or at least having a video call — is important. She told me:

Nadieh Bremer

“As a remote freelancer, I know that to interact well with my clients I really need to have a video call (stress on the video) I need to see their face and facial/body interactions and they need to see mine. For clients that I have within public transport distance, I used to travel there for a first ‘getting to know each other/see if we can do a project’ meeting, which would take loads of time. But I noticed for my clients abroad (that I can’t visit anyway) that a first client call (again, make sure it’s a video-call) works more than good enough.

It’s the perfect way to weed out the clients that need other skills that I can give, those that are looking for a cheap deal, and those where I just felt something wasn’t quite clicking or I’m not enthusiastic about the project after they’ve given me a better explanation. So these days I also ask my clients in the Netherlands, where I live, that might want to do a first meeting to have it online (and once we get on to an actual contract I can come by if it’s beneficial).”

Working In The Open

Working in the open (with the project frequently deployed to a staging server that everyone had access to see), helped to support an iterative approach to development. I found that it was important to support that live version with explanations and notes of what to look at and test and what was still half finished. If I just invited people to look at it without that information we would get lists of fixes to make to unfinished features, which is a waste of time for the person doing the reporting. However, a live staging version, plus notes in a collaboration tool such as Basecamp meant that we could deploy sections and post asking for feedback on specific things. This helped to keep everyone up to date and part of the project even if — as was often the case for designers in an agency — they had a number of other projects to work on.

There are collaboration tools to help designers to share their work too. Asking for recommendations on Twitter gave me suggestions for Zeplin, Invision, Figma, and Adobe XD. Showing work in progress to a developer can help them to catch things that might be tricky before they are signed off by the client. By sharing the goal behind a particular design feature within the team, a way forward can be devised that meets the goal without blowing the budget.


Screenshot of the Zeplin homepage


Zeplin is a collaboration tool for developers and designers

Scope Creep And Change Requests

The thing about working in the open is that people then start to have ideas (which should be a positive thing), however, most timescales and budgets are not infinite! This means you need to learn to deal with scope creep and change requests in a way that maintains a good working relationship.

We would often get requests for things that were trivial to implement with a message saying how sorry they were about this huge change and requests for incredibly time-consuming things with an assumption it would be quick. Someone who is not a specialist has no idea how long anything will take. Why should they? It is important to remember this rather than getting frustrated about the big changes that are being asked for. Have a conversation about the change, explain why it is more complex than it might appear, and try to work out whether this is a vital addition or change, or just a nice idea that someone has had.

If the change is not essential, then it may be enough to log it somewhere as a phase two request, demonstrating that it has been heard and won’t be forgotten. If the big change is still being requested, we would outline the time it would take and give options. This might mean dropping some other feature if a project has a fixed budget and tight deadline. If there was flexibility then we could outline the implications on both costs and end date.

With regard to costs and timescales, we learned early on to pad our project quotes in order that we could absorb some small changes without needing to increase costs or delay completion. This helped with the relationship between the agency and ourselves as they didn’t feel as if they were being constantly nickel and dimed. Small changes were expected as part of the process of development. I also never wrote these up in a quote as contingency, as a client would read that and think they should be able to get the project done without dipping into the contingency. I just added the time to the quote for the overall project. If the project ran smoothly and we didn’t need that time and money, then the client got a smaller bill. No one is ever unhappy about being invoiced for less than they expected!

This approach can work even for people working in-house. Adding some time to your estimates means that you can absorb small changes without needing to extend the timescales. It helps working relationships if you are someone who is able to say yes as often as possible.

This does require that you become adept at estimating timescales. This is a skill you can develop by logging your time to achieve your work, even if you don’t need to log your time for work purposes. While many of the things you design or develop will be unique, and seem impossible to estimate, by consistently logging your time you will generally find that your ballpark estimates become more accurate as you make yourself aware of how long things really take.

Respect

Aaron Draplin will be bringing tales from his career in design to Toronto, and responded with the thought that it comes down to respect for your colleague’s craft:

Aaron Draplin

“It all comes down to respect for your colleague’s craft, and sort of knowing your place and precisely where you fit into the project. When working with a developer, I surrender to them in a creative way, and then, defuse whatever power play they might try to make on me by leading the charges with constructive design advice, lightning-fast email replies and generally keeping the spirit upbeat. It’s an odd offense to play. I’m not down with the adversarial stuff. I’m quick to remind them we are all in the same boat, and, who’s paying their paycheck. And that’s not me. It’s the client. I’ll forever be on their team, you know? We make the stuff for the client. Not just me. Not ‘my team’. We do it together. This simple methodology has always gone a long way for me.”

I love this, it underpins everything that this article discusses. Think back to any working relationship that has gone bad, how many of those involved you feeling as if the other person just didn’t understand your point of view or the things you believe are important? Most reasonable people understand that compromise has to be made, it is when it appears that your point of view is not considered that frustration sets in.

There are sometimes situations where a decision is being made, and your experience tells you it is going to result in a bad outcome for the project, yet you are overruled. On a few occasions, decisions were made that I believed so poor; I asked for the decision and our objection to it be put in writing, in order that we could not be held accountable for any bad outcome in future. This is not something you should feel the need to do often, however, it is quite powerful and sometimes results in the decision being reversed. An example would be of a client who keeps insisting on doing something that would cause an accessibility problem for a section of their potential audience. If explaining the issue does not help, and the client insists on continuing, ask for that decision in writing in order to document your professional advice.

Learning The Language

I recently had the chance to bring my CSS Layout Workshop not to my usual groups of front-end developers but instead to a group of UX designers. Many of the attendees were there not to improve their front-end development skills, but more to understand enough of how modern CSS Layout worked that they could have better conversations with the developers who built their designs. Many of them had also spent years being told that certain things were not possible on the web, but were realizing that the possibilities in CSS were changing through things like CSS Grid. They were learning some CSS not necessarily to become proficient in shipping it to production, but so they could share a common language with developers.

There are often debates on whether “designers should learn to code.” In reality, I think we all need to learn something of the language, skills, and priorities of the other people on our teams. As Aaron reminded us, we are all on the same team, we are making stuff together. Designers should learn something about code just as developers should also learn something of design. This gives us more of a shared language and understanding.

Seb Lee-Delisle, who will speak on the subject of Hack to the Future in Toronto, agrees:

Seb Lee-Delisle

“I have basically made a career out of being both technical and creative so I strongly feel that the more crossover the better. Obviously what I do now is wonderfully free of the constraints of client work but even so, I do think that if you can blur those edges, it’s gonna be good for you. It’s why I speak at design conferences and encourage designers to play with creative coding, and I speak at tech conferences to persuade coders to improve their visual acuity. Also with creative coding. :) It’s good because not only do I get to work across both disciplines, but also I get to annoy both designers and coders in equal measure.”

I have found that introducing designers to browser DevTools (in particular the layout tools in Firefox and also to various code generators on the web) has been helpful. By being able to test ideas out without writing code, helps a designer who isn’t confident in writing code to have better conversations with their developer colleagues. Playing with tools such as gradient generators, clip-path or animation tools can also help designers see what is possible on the web today.


Screenshot of Animista


Animista has demos of different styles of animation

We are also seeing a number of tools that can help people create websites in a more visual way. Developers can sometimes turn their noses up about the code output of such tools, and it’s true they probably won’t be the best choice for the production code of a large project. However, they can be an excellent way for everyone to prototype ideas, without needing to write code. Those prototypes can then be turned into robust, permanent and scalable versions for production.

An important tip for developers is to refrain from commenting on the code quality of prototypes from members of the team who do not ship production code! Stick to what the prototype is showing as opposed to how it has been built.

A Practical Suggestion To Make Things Visual

Eva-Lotta Lamm will be speaking in Toronto about Sketching and perhaps unsurprisingly passed on practical tips for helping conversation by visualizing the problem to support a conversation.

Eva-Lotta Lamm

Creating a shared picture of a problem or a solution is a simple but powerful tool to create understanding and make sure they everybody is talking about the same thing.

Visualizing a problem can reach from quick sketches on a whiteboard to more complex diagrams, like customer journey diagrams or service blueprints.

But even just spatially distributing words on a surface adds a valuable layer of meaning. Something as simple as arranging post-its on a whiteboard in different ways can help us to see relationships, notice patterns, find gaps and spot outliers or anomalies. If we add simple structural elements (like arrows, connectors, frames, and dividers) and some sketches into the mix, the relationships become even more obvious.

Visualising a problem creates context and builds a structural frame that future information, questions, and ideas can be added to in a ‘systematic’ way.

Visuals are great to support a conversation, especially when the conversation is ‘messy’ and several people involved.

When we visualize a conversation, we create an external memory of the content, that is visible to everybody and that can easily be referred back to. We don’t have to hold everything in our mind. This frees up space in everybody’s mind to think and talk about other things without the fear of forgetting something important. Visuals also give us something concrete to hold on to and to follow along while listening to complex or abstract information.

When we have a visual map, we can point to particular pieces of content — a simple but powerful way to make sure everybody is talking about the same thing. And when referring back to something discussed earlier, the map automatically reminds us of the context and the connections to surrounding topics.

When we sketch out a problem, a solution or an idea the way we see it (literally) changes. Every time we express a thought in a different medium, we are forced to shape it in a specific way, which allows us to observe and analyze it from different angles.

Visualising forces us to make decisions about a problem that words alone don’t. We have to decide where to place each element, decide on its shape, size, its boldness, and color. We have to decide what we sketch and what we write. All these decisions require a deeper understanding of the problem and make important questions surface fairly quickly.

All in all, supporting your collaboration by making it more visual works like a catalyst for faster and better understanding.

Working in this way is obviously easier if your team is working in the same room. For distributed teams and freelancers, there are alternatives to communicate in ways other than words, e.g. by making a quick Screencast to demonstrate an issue, or even sketching and photographing a diagram can be incredibly helpful. There are collaborative tools such as Milanote, Mural, and Niice; such tools can help with the process Eva-Lotta described even if people can’t be in the same room.


Screenshot of the Niice website


Niice helps you to collect and discuss ideas

I’m very non-visual and have had to learn how useful these other methods of communication are to the people I work with. I have been guilty on many occasions of forgetting that just because I don’t personally find something useful, it is still helpful to other people. It is certainly a good idea to change how you are trying to communicate an idea if it becomes obvious that you are talking at cross-purposes.

Over To You

As with most things, there are many ways to work together. Even for remote teams, there is a range of tools which can help break down barriers to collaborating in a more visual way. However, no tool is able to fix problems caused by a lack of respect for the work of the rest of the team. A good relationship starts with the ability for all of us to take a step back from our strongly held opinions, listen to our colleagues, and learn to compromise. We can then choose tools and workflows which help to support that understanding that we are all on the same team, all trying to do a great job, and all have important viewpoints and experience to bring to the project.

I would love to hear your own experiences working together in the same room or remotely. What has worked well — or not worked at all! Tools, techniques, and lessons learned are all welcome in the comments. If you would be keen to see tutorials about specific tools or workflows mentioned here, perhaps add a suggestion to our User Suggestions board, too.

Smashing Editorial
(il)


Follow this link: 

Working Together: How Designers And Developers Can Communicate To Create Better Projects

Smart Responsive UX Design Patterns

Full-day workshop • April 16th
In this brand new workshop, Vitaly Friedman will cover practical techniques, clever tricks and useful strategies you need to be aware of when working on responsive websites. From responsive modules to clever navigation patterns and web form design techniques; the workshop will provide you with everything you need to know today to start designing better responsive experiences tomorrow.
Most techniques are borrowed from mid-size and large-scale real-life projects, such as large eCommerce projects, online magazines and web applications.

See the article here:

Smart Responsive UX Design Patterns

Automating Your Feature Testing With Selenium WebDriver




Automating Your Feature Testing With Selenium WebDriver

Nils Schütte



This article is for web developers who wish to spend less time testing the front end of their web applications but still want to be confident that every feature works fine. It will save you time by automating repetitive online tasks with Selenium WebDriver. You will find a step-by-step example for automating and testing the login function of WordPress, but you can also adapt the example for any other login form.

What Is Selenium And How Can It Help You?

Selenium is a framework for the automated testing of web applications. Using Selenium, you can basically automate every task in your browser as if a real person were to execute the task. The interface used to send commands to the different browsers is called Selenium WebDriver. Implementations of this interface are available for every major browser, including Mozilla Firefox, Google Chrome and Internet Explorer.

Automating Your Feature Testing With Selenium WebDriver

Which type of web developer are you? Are you the disciplined type who tests all key features of your web application after each deployment. If so, you are probably annoyed by how much time this repetitive testing consumes. Or are you the type who just doesn’t bother with testing key features and always thinks, “I should test more, but I’d rather develop new stuff.” If so, you probably only find bugs by chance or when your client or boss complains about them.

I have been working for a well-known online retailer in Germany for quite a while, and I always belonged to the second category: It was so exciting to think of new features for the online shop, and I didn’t like at all going over all of the previous features again after each new software deployment. So, the strategy was more or less to hope that all key features would work.

One day, we had a serious drop in our conversion rate and started digging in our web analytics tools to find the source of this drop. It took quite a while before we found out that our checkout did not work properly since the previous software deployment.

This was the day when I started to do some research about automating our testing process of web applications, and I stumbled upon Selenium and its WebDriver. Selenium is basically a framework that allows you to automate web browsers. WebDriver is the name of the key interface that allows you to send commands to all major browsers (mobile and desktop) and work with them as a real user would.

Preparing The First Test With Selenium WebDriver

First, I was a little skeptical of whether Selenium would suit my needs because the framework is most commonly used in Java, and I am certainly not a Java expert. Later, I learned that being a Java expert is not necessary to take advantage of the power of the Selenium framework.

As a simple first test, I tested the login of one of my WordPress projects. Why WordPress? Just because using the WordPress login form is an example that everybody can follow more easily than if I were to refer to some custom web application.

What do you need to start using Selenium WebDriver? Because I decided to use the most common implementation of Selenium in Java, I needed to set up my little Java environment.

If you want to follow my example, you can use the Java environment of your choice. If you haven’t set one up yet, I suggest installing Eclipse and making sure you are able to run a simple “Hello world” script in Java.

Because I wanted to test the login in Chrome, I made sure that the Chrome browser was already installed on my machine. That’s all I did in preparation.

Downloading The ChromeDriver

All major browsers provide their own implementation of the WebDriver interface. Because I wanted to test the WordPress login in Chrome, I needed to get the WebDriver implementation of Chrome: ChromeDriver.

I extracted the ZIP archive and stored the executable file chromedriver.exe in a location that I could remember for later.

Setting Up Our Selenium Project In Eclipse

The steps I took in Eclipse are probably pretty basic to someone who works a lot with Java and Eclipse. But for those like me, who are not so familiar with this, I will go over the individual steps:

  1. Open Eclipse.
  2. Click the “New” icon.
    Creating a new project in Eclipse
    Creating a new project in Eclipse
  3. Choose the wizard to create a new “Java Project,” and click “Next.”
    Chosing the java-project wizard
    Choose the java-project wizard.
  4. Give your project a name, and click “Finish.”
    Eclipse project wizard
    The eclipse project wizard
  5. Now you should see your new Java project on the left side of the screen.
    Java project successfully created
    We successfully created a project to run the Selenium WebDriver.

Adding The Selenium Library To Our Project

Now we have our Java project, but Selenium is still missing. So, next, we need to bring the Selenium framework into our Java project. Here are the steps I took:

  1. Download the latest version of the Java Selenium library.

    Downloading the Selenium library
    Download the Selenium library.
  2. Extract the archive, and store the folder in a place you can remember easily.
  3. Go back to Eclipse, and go to “Project” → “Properties.”
    Eclipse Properties
    Go to properties to integrate the Selenium WebDriver in you project.
  4. In the dialog, go to “Java Build Path” and then to register “Libraries.”
  5. Click on “Add External JARs.”
    Adding the Selenium lib to your Java build path.
    Add the Selenium lib to your Java build path.
  6. Navigate to the just downloaded folder with the Selenium library. Highlight all .jar files and click “Open.”
    Selecting the correct files of the Selenium lib.
    Select all files of the lib to add to your project.
  7. Repeat this for all .jar files in the subfolder libs as well.
  8. Eventually, you should see all .jar files in the libraries of your project:
    Selenium WebDriver framework successfully integrated into your project
    The Selenium WebDriver framework has now been successfully integrated into your project!

That’s it! Everything we’ve done until now is a one-time task. You could use this project now for all of your different tests, and you wouldn’t need to do the whole setup process for every test case again. Kind of neat, isn’t it?

Creating Our Testing Class And Letting It Open the Chrome Browser

Now we have our Selenium project, but what next? To see whether it works at all, I wanted to try something really simple, like just opening my Chrome browser.

To do this, I needed to create a new Java class from which I could execute my first test case. Into this executable class, I copied a few Java code lines, and believe it or not, it worked! Magically, the Chrome browser opened and, after a few seconds, closed all by itself.

Try it yourself:

  1. Click on the “New” button again (while you are in your new project’s folder).
    New class in eclipse
    Create a new class to run the Selenium WebDriver.
  2. Choose the “Class” wizard, and click “Next.”
    New class wizard in eclipse
    Choose the Java class wizard to create a new class.
  3. Name your class (for example, “RunTest”), and click “Finish.”
    Eclipse Java Class wizard
    The eclipse Java Class wizard.
  4. Replace all code in your new class with the following code. The only thing you need to change is the path to chromedriver.exe on your computer:
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.chrome.ChromeDriver;
    
    /**
     * @author Nils Schuette via frontendtest.org
     */
    public class RunTest 
        static WebDriver webDriver;
        /**
         * @param args
         * @throws InterruptedException
         */
        public static void main(final String[] args) throws InterruptedException 
            // Telling the system where to find the chrome driver
            System.setProperty(
                    "webdriver.chrome.driver",
                    "C:/PATH/TO/chromedriver.exe");
    
            // Open the Chrome browser
            webDriver = new ChromeDriver();
    
            // Waiting a bit before closing
            Thread.sleep(7000);
    
            // Closing the browser and WebDriver
            webDriver.close();
            webDriver.quit();
        
    }
    
  5. Save your file, and click on the play button to run your code.
    Run Eclipse project
    Running your first Selenium WebDriver project.
  6. If you have done everything correctly, the code should open a new instance of the Chrome browser and close it shortly thereafter.
    Chrome Browser blank window
    The Chrome Browser opens itself magically. (Large preview)

Testing The WordPress Admin Login

Now I was optimistic that I could automate my first little feature test. I wanted the browser to navigate to one of my WordPress projects, login to the admin area and verify that the login was successful. So, what commands did I need to look up?

  1. Navigate to the login form,
  2. Locate the input fields,
  3. Type the username and password into the input fields,
  4. Hit the login button,
  5. Compare the current page’s headline to see if the login was successful.

Again, after I had done all the necessary updates to my code and clicked on the run button in Eclipse, my browser started to magically work itself through the WordPress login. I successfully ran my first automated website test!

If you want to try this yourself, replace all of the code of your Java class with the following. I will go through the code in detail afterwards. Before executing the code, you must replace four values with your own:

  1. The location of your chromedriver.exe file (as above),

  2. The URL of the WordPress admin account that you want to test,

  3. The WordPress username,

  4. The WordPress password.

Then, save and let it run again. It will open Chrome, navigate to the login of your WordPress website, login and check whether the h1 headline of the current page is “Dashboard.”

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

/**
 * @author Nils Schuette via frontendtest.org
 */
public class RunTest 
    static WebDriver webDriver;
    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(final String[] args) throws InterruptedException 
        // Telling the system where to find the chrome driver
        System.setProperty(
                "webdriver.chrome.driver",
                "C:/PATH/TO/chromedriver.exe");

        // Open the Chrome browser
        webDriver = new ChromeDriver();

        // Maximize the browser window
        webDriver.manage().window().maximize();

        if (testWordpresslogin()) 
            System.out.println("Test WordPress Login: Passed");
         else 
            System.out.println("Test WordPress Login: Failed");

        

        // Close the browser and WebDriver
        webDriver.close();
        webDriver.quit();
    }

    private static boolean testWordpresslogin() 
        try 
            // Open google.com
            webDriver.navigate().to("https://www.YOUR-SITE.org/wp-admin/");

            // Type in the username
            webDriver.findElement(By.id("user_login")).sendKeys("YOUR_USERNAME");

            // Type in the password
            webDriver.findElement(By.id("user_pass")).sendKeys("YOUR_PASSWORD");

            // Click the Submit button
            webDriver.findElement(By.id("wp-submit")).click();

            // Wait a little bit (7000 milliseconds)
            Thread.sleep(7000);

            // Check whether the h1 equals “Dashboard”
            if (webDriver.findElement(By.tagName("h1")).getText()
                    .equals("Dashboard")) 
                return true;
             else 
                return false;
            

        // If anything goes wrong, return false.
        } catch (final Exception e) 
            System.out.println(e.getClass().toString());
            return false;
        
    }
}

If you have done everything correctly, your output in the Eclipse console should look something like this:

Eclipse console test result.
The Eclipse console states that our first test has passed. (Large preview)

Understanding The Code

Because you are probably a web developer and have at least a basic understanding of other programming languages, I am sure you already grasp the basic idea of the code: We have created a separate method, testWordpressLogin, for the specific test case that is called from our main method.

Depending on whether the method returns true or false, you will get an output in your console telling you whether this specific test passed or failed.

This is not necessary, but this way you can easily add many more test cases to this class and still have readable code.

Now, step by step, here is what happens in our little program:

  1. First, we tell our program where it can find the specific WebDriver for Chrome.
    System.setProperty("webdriver.chrome.driver","C:/PATH/TO/chromedriver.exe");
  2. We open the Chrome browser and maximize the browser window.
    webDriver = new ChromeDriver();
    webDriver.manage().window().maximize();
  3. This is where we jump into our submethod and check whether it returns true or false.
    if (testWordpresslogin()) …
  4. The following part in our submethod might not be intuitive to understand:
    The try…catch… blocks. If everything goes as expected, only the code in try… will be executed, but if anything goes wrong while executing try…, then the execution continuous in catch{}. Whenever you try to locate an element with findElement and the browser is not able to locate this element, it will throw an exception and execute the code in catch…. In my example, the test will be marked as “failed” whenever something goes wrong and the catch{} is executed.
  5. In the submethod, we start by navigating to our WordPress admin area and locating the fields for the username and the password by looking for their IDs. Also, we type the given values in these fields.
    webDriver.navigate().to("https://www.YOUR-SITE.org/wp-admin/");
    webDriver.findElement(By.id("user_login")).sendKeys("YOUR_USERNAME");
    webDriver.findElement(By.id("user_pass")).sendKeys("YOUR_PASSWORD");

    Wordpress login form
    Selenium fills out our login form
  6. After filling in the login form, we locate the submit button by its ID and click it.
    webDriver.findElement(By.id("wp-submit")).click();
  7. In order to follow the test visually, I include a 7-second pause here (7000 milliseconds = 7 seconds).
    Thread.sleep(7000);
  8. If the login is successful, the h1 headline of the current page should now be “Dashboard,” referring to the WordPress admin area. Because the h1 headline should exist only once on every page, I have used the tag name here to locate the element. In most other cases, the tag name is not a good locator because an HTML tag name is rarely unique on a web page. After locating the h1, we extract the text of the element with getText() and check whether it is equal to the string “Dashboard.” If the login is not successful, we would not find “Dashboard” as the current h1. Therefore, I’ve decided to use the h1 to check whether the login is successful.
    if (webDriver.findElement(By.tagName("h1")).getText().equals("Dashboard")) 
        
            return true;
         else 
            return false;
        
    

    Wordpress Dashboard
    Letting the WebDriver check, whether we have arrived on the Dashboard: Test passed! (Large preview)
  9. If anything has gone wrong in the previous part of the submethod, the program would have jumped directly to the following part. The catch block will print the type of exception that happened to the console and afterwards return false to the main method.
    catch (final Exception e) 
                System.out.println(e.getClass().toString());
                return false;
            

Adapting The Test Case

This is where it gets interesting if you want to adapt and add test cases of your own. You can see that we always call methods of the webDriver object to do something with the Chrome browser.

First, we maximize the window:

webDriver.manage().window().maximize();

Then, in a separate method, we navigate to our WordPress admin area:

webDriver.navigate().to("https://www.YOUR-SITE.org/wp-admin/");

There are other methods of the webDriver object we can use. Besides the two above, you will probably use this one a lot:

webDriver.findElement(By. …)

The findElement method helps us find different elements in the DOM. There are different options to find elements:

  • By.id
  • By.cssSelector
  • By.className
  • By.linkText
  • By.name
  • By.xpath

If possible, I recommend using By.id because the ID of an element should always be unique (unlike, for example, the className), and it is usually not affected if the structure of your DOM changes (unlike, say, the xPath).

Note: You can read more about the different options for locating elements with WebDriver over here.

As soon as you get ahold of an element using the findElement method, you can call the different available methods of the element. The most common ones are sendKeys, click and getText.

We’re using sendKeys to fill in the login form:

webDriver.findElement(By.id("user_login")).sendKeys("YOUR_USERNAME");

We have used click to submit the login form by clicking on the submit button:

webDriver.findElement(By.id("wp-submit")).click();

And getText has been used to check what text is in the h1 after the submit button is clicked:

webDriver.findElement(By.tagName("h1")).getText()

Note: Be sure to check out all the available methods that you can use with an element.

Conclusion

Ever since I discovered the power of Selenium WebDriver, my life as a web developer has changed. I simply love it. The deeper I dive into the framework, the more possibilities I discover — running one test simultaneously in Chrome, Internet Explorer and Firefox or even on my smartphone, or taking screenshots automatically of different pages and comparing them. Today, I use Selenium WebDriver not only for testing purposes, but also to automate repetitive tasks on the web. Whenever I see an opportunity to automate my work on the web, I simply copy my initial WebDriver project and adapt it to the next task.

If you think that Selenium WebDriver is for you, I recommend looking at Selenium’s documentation to find out about all of the possibilities of Selenium (such as running tasks simultaneously on several (mobile) devices with Selenium Grid).

I look forward to hearing whether you find WebDriver as useful as I do!

Smashing Editorial
(rb, ra, al, il)


Original link: 

Automating Your Feature Testing With Selenium WebDriver

Finding UX Research Participants




Finding UX Research Participants

For UX designers and design teams, research with stakeholders and users is critical. However, accessing research participants isn’t as easy as it sounds. For both professional and amateur researchers finding people to participate in studies can be an elusive task. We often hear about studies and their findings, but we don’t hear as often how researchers recruit study participants.

Researchers can choose from a variety ways to find participants. Many factors determine the best method to use. This includes resources such as time and money, the research method you’re using, the type or characteristics of participants you want to recruit, and the accessibility of these types of participants. In this post, I’ll remove some of the mystery and provide guidance to those interested in recruiting participants for qualitative UX studies.


Potential research participants are everywhere, if you know what to look for.


Potential research participants are everywhere, if you know what to look for.

Incentives

You can use incentives to increase the likelihood of participation in any of these methods of recruitment. Use of incentives is usually a personal choice. Do you feel incentivized participants provide skewed or biased data? I don’t have any issues with providing incentives. An incentive can be a small token of appreciation ($5 gift card) or something more substantial ($200 or more depending on time needed and type of participant.

I’ve provided guidance for each method based on my experience with incentives.

Identifying And Interviewing Key Internal Stakeholders

You gain insight when you interview key colleagues, clients, and other relevant stakeholders of a project. Particularly at the beginning of a project. This is a great opportunity to understand everyone’s role, what their vision and hopes are for a project or product, and how you might incorporate their experience into the rest of the project. You can increase buy-in and make people feel like part of the process by including stakeholder interviews in any project. I use the term internal stakeholder broadly to describe individuals who have a vested interest in a product or project who are connected to your organization or the product in some way. Many of these internal stakeholders might also be users of the product you are interviewing them about.

When To Use It

You can always look for opportunities to interview stakeholders and colleagues to learn more. This is especially useful at the beginning of a project. You can learn expectations for a product, background information on what led to the current status of the project, and goals and hopes for the future. Checking in with stakeholders throughout a project will keep them aware of how things are progressing and allow you to get their feedback. I’ve found this is helpful for building trust with stakeholders and making them feel included in the process.

How You Might Do It

You can often arrange interviews with key stakeholders yourself if they are internal to your company. You’ll identify who is relevant to your project, including project team members, and invite them to an interview. You can contact them to schedule a time, or look to schedule using your company’s shared calendar platform (e.g., Outlook or G Suite). You should know ahead of time how long you need to schedule and how you will interview the participant (in-person or remote) so you can share this information with them at scheduling.

Identifying and interviewing stakeholders becomes more complicated when you don’t have direct access to scheduling yourself. If your project team is part of a larger organization, you might need to ask colleagues in other departments to help identify and schedule stakeholders. If you are on a project team with outside partners or have external stakeholders, you will often need someone to facilitate identification and scheduling of interviews. I’ll cover some additional challenges for recruiting stakeholders and others through your clients in the identifying participants through a client section.

Positive Aspects

Gain insight into roles, backgrounds, and history of stakeholders’ involvement with a product or issue, potentially quick to schedule, low to no cost outside of time, can be done remote or in-person, talking to customer/user-facing stakeholders might provide some insight into what users think of a product.

Negative Aspects

Difficult to identify everyone you want to participate, might include people at high-levels who are hard to reach, scheduling if not doing it yourself, scaling down if resources are limited, does not replace research with users, many stakeholders are too close to their product to be objective.

Incentives

I typically don’t provide an incentive if internal stakeholders are participating during work hours.

Case Study

I worked on a project with a bank that wanted to design an online onboarding experience for new customers. We needed to understand what the current (non-digital) onboarding experience was. We wanted to document available resources to pull into the onboarding experience. Lastly, we needed to build trust with partners who we were going to rely on to champion the experience we created.

We relied on word of mouth to learn who we needed to speak with. First, we interviewed the people we were closest to and asked them who else they considered necessary for us to speak with. We spoke with people in numerous US states, both remotely and in-person. We were able to speak with 30 people in three weeks (this was not a project we were dedicating full time). Occasionally, we spoke to people who were not relevant to our specific purpose. There were two key reasons we were given names of some folks who weren’t relevant:

  • They were higher up executives with little knowledge of what we were exploring
  • The people referring them didn’t understand/effectively convey what we were trying to accomplish, so they volunteered to participate in something not aligned with their role

We found our most common difficulties were in scheduling and getting people to reply to our initial emails. We were trying to schedule an hour to speak with people who spend most of their days traveling and in meetings. Many of them had personal assistants managing their calendars. Some didn’t have an opening to speak with us for weeks after our initial request. Most people did want to make the time to speak with us. They viewed our project as one with high strategic importance in the long-term health of their company. We also had many people reschedule due to unforeseen conflicts involving client needs arising.

We were able to paint a clearer picture of the bank’s onboarding experience and what resources were available. We were able to understand what (some) of the leadership viewed as the potential future for an onboarding experience with new customers and what their perceptions of shortcomings were for the current onboarding experience. We were able to identify gaps in knowledge that required additional future research and education. We made connections with critical internal advocates who walked away with a better understanding and appreciation of the experience we were creating. We would not have been able to achieve these outcomes through a survey or through other means of recruiting participants. Later, we were able to approach these same stakeholders to have them provide feedback on the designs for the onboarding experience we created.

Identifying Participants Through A Client

Many potential research participants are unavailable to the general public. You will find situations where you don’t have direct access to recruiting relevant participants. This is particularly true if you work for a design consultancy/studio, or as part of a shared services team within a large organization. For example, if your client is a widget manufacturer and their product is a widget warehouse product supply application, you will need to access their staff in order to understand their current pain points and needs. You won’t have an easy time finding relevant participants using the population you have access to. You want to conduct research and usability testing with participants who will become the end user of the application, which again means you’d need to access this population through your client.

When To Use It

In addition to the reasons given in the previous section for recruiting stakeholders, when you have to reach specific populations, need opinions from specific people, and want to make your client-stakeholders feel like part of the process. When you don’t have direct insight or access to critical research participants when you are looking to build relationships beyond the project team you are working with, when you want to include a diverse set of individuals covering relevant areas of the product you’re working on.

How To Do It

Work closely with your client or person you are collaborating with to identify the right people for the project you are on. Your project will dictate the exact specifications of roles you need. This includes Product Owners, VPs, Business Analysts, and Users. I often provide a script or email language for my clients to use for recruiting participants. I explain the purpose of the research, how you were made aware of the participant (e.g., Jane from accounting gave me your information) how long the conversation is expected to take, potential dates of availability, incentive (if any), prep work required (if any).

You should provide your client with a screener clearly stating:

  • How many of each type of participant you want to participate
  • Details you want to know ahead of time (e.g., years using the product, industry)
  • Factors leading to disqualification from the study (e.g., less than one year of experience with the product)

Bonus: Many organizations keep data on their users. Your client might be able to screen their database and provide you research participants. However, when I’ve used this in the past, there are often many permissions required and processes to gain access to customers. This can add a significant amount of time to your project.

I am always clear to my clients that scheduling participants is one of the largest hurdles to a project’s timeline. Working with others’ schedules is complicated. You should make it clear to your clients how to recruit, and the need to start recruiting as soon as possible.

Positive Aspects

You get specific people close to a project or product, you learn about long-term and short-term goals directly from the people you work with, you are able to ask to follow up questions that might inform projects well beyond your current relationship, you learn the history of the product or organization, you can reach relevant people you don’t have direct access to, you gain insight into roles, backgrounds, and history of stakeholders’ and users’ involvement with an issue, you will find talking directly to the users of the product provides context and texture you wouldn’t find from someone without similar knowledge.

Negative Aspects

This can be time-consuming, requires a clear communication of purpose, you might end up talking to people less relevant if your client doesn’t screen effectively, less control over scheduling, lack of control over how information is shared with participants.

Incentives – I typically don’t provide an incentive if they are from the client and participating during work hours. I’d provide an incentive if they have recruited users who are coming in on an off day or outside of work hours. You might also have a larger incentive but only give it to a couple randomly selected participants.

Case Study

I worked for a team looking at redesigning a digital report for a large mortgage lender. Many other banks and loan providers do business under the umbrella of this company. We needed to identify a specific type of user, one who: worked for a bank under the parent company and used the report as part of their daily tasks.

The client wanted us to interview 30 individuals with roles interacting with the report. They identified a handful of these individuals upfront, and then put out a call for participation to identify the remaining individuals. There were numerous layers of communication through relationship managers as well as permissions and disclosures the client needed to handle with each participant.

We were able to complete over 30 remote (over the phone) interviews in the month we were allotted to collect data. Our client arranged and scheduled each interview. Our most common difficulties were similar to those I gave in the previous case study, scheduling and relevancy of participants. We were interviewing people who spend their entire workday running the report and using the data to inform their decisions; busy people with limited flexibility of daytime work hours. We made ourselves available at any time a participant had availability in order to solve this. This created drawbacks in scheduling other meetings unrelated to working on the project.

Some of our participants forwarded the invitation to others they thought should be on the interview as well. We would find this out when more than one person would join the call. We were initially caught off guard when we had a call intended for one person take place with four participants at once. We created a separate multi-participant protocol to account for this occurring on future calls, which it did. I recommend expecting this to happen regardless of who is recruiting your participants. It’s difficult to control what happens, once you send out an invitation to the wild.

We used data from our interviews to understand the current behaviors, frustrations, and needs of users. We also presented later participants with sample designs in order to get feedback on report layout and feature changes. We delivered a redesigned report that exceeded client expectations and became a reference piece in their quest to get further funding for research and design projects.

Paying A Recruitment Firm (When You Have An Accessible Population)

Recruitment firms offer services ranging from participant screening and recruitment, facilities to conduct research, recording your sessions, and much more. You can use a recruitment firm when you are conducting research with populations you believe you can reach through contact with the general public. For example, if you are conducting usability testing on an online banking application. You can expect most people familiar with banking transactions (e.g., making a deposit or bill pay) should successfully use your application. Even if they don’t currently use your bank.

I’ve used a number of firms over the past few years. Most of them offer similar services.


Recruitment firms often provide facilities for interviews or usability testing.


Recruitment firms often provide facilities for interviews or usability testing.

When To Use It

When you don’t have direct access to potential participants when you want to have a third party screen your participants, when your sample is available through the general public, when you want to have someone handle recruitment, scheduling, and day-of-research preparation.

How To Do It

You will need to create the screener the recruiter will use. You decide in advance how many of each type of participant you will want. You’ll want to include a number of “floaters” in your recruitment as well. Floaters are people who meet the requirements of the study and are willing to show up for participation in case some of the other participants don’t show up. Floaters are typically compensated at higher levels because they are committing to spend two or three hours sitting around in case they are needed.

You’ll also need to provide the screener with enough advance notice as the recruiter requires. I’ve found this is two weeks in advance for most studies, and three weeks in advance for more complex studies. All recruitment firms offer participants an incentive, usually cash, to participate in a study. You will have to be ok with the fact your participants are receiving money to participate. I haven’t found this to be problematic, but you should be prepared to defend why you don’t think this will add any additional bias to your data.

Positive Aspects

Very detailed screening, don’t have to find people, often have a facility you can use, will record audio and video as needed, will recruit additional participants in case some don’t show up.

Negative Aspects

Cost, the time needed in advance if you have a difficult to reach population, participants trying to game the system.

Incentives – Recruitment firms almost always compensate the people they recruit. You will pay the recruitment firm a set fee they pay to participants.

Case Study

I worked for a team wanting to define the digital needs and behaviors of specific types of Financial Advisors. The client did not want to expose their brand during any of the research, so they did not want to facilitate the recruitment. The client wanted the interviews to pull participants from more than one major city in the US. We worked with a recruitment firm to identify and recruit participants, as well as to conduct the interview sessions.

We worked with the client to create a detailed screener with items meant to refine the population to the specific participants we wanted for the study. The recruitment firm asked for three weeks to find 15 participants for the first city in our study. The usual turn around when working with the firm was two weeks with less specialized participants. We were also advised to provide a higher incentive, over double what we typically offered, due to the probability we were asking participants to step away from work and the perceived value of their time.

We were able to interview 15 participants over the course of two days. We found a few of the participants didn’t actually meet the qualifications we’d screened for. They had manipulated their responses to qualify. Our client was unhappy with this. We were able to use the floaters to replace the participants who didn’t truly qualify. We were also able to get a refund on what we’d paid to recruit the unqualified participants.

Ultimately, we reached our goal of interviewing the right number of participants in the right amount of time, and produced a report on needs and behaviors for our client.

We would not have been able to access this population without the use of the recruitment firm. The client was unwilling to expose their brand and therefore unwilling to identify participants from their contact list. We would have spent more time and money than the project allowed if we were left to recruit participants. We don’t have contact lists or the ability to easily identify specialized populations through our own resources. We still experienced frustration with the lack of initial quality participants the recruitment firm provided. In general, we’ve had positive experiences with recruitment firms, but the more specialized the population, the more likely you will find some duds.

Guerilla Recruiting (When You Want To Find People In The Wild)

You can utilize public spaces to recruit potential study participants. Guerilla research is a term for quick and dirty research conducted with people as they go about their daily tasks (in the wild so to speak). The term is meant to reflect a context in which you are pressed for resources. However, you can benefit from using this method of recruiting even when you have resources for other methods. Sometimes collecting data from people when they are in specific settings is the most appropriate method.


You can find plenty of potential users in the wild.


You can find plenty of potential users in the wild.

You should determine a space you want to recruit participants for a logical reason. Let’s say you’re designing a smartphone application meant to help people track their workouts at the gym. You would want to recruit participants from that setting, entering or exiting the gym. If you wanted to test out a new form of electronic payment, you’d want to be present in a setting where transactions take place.

When To Use It

When you have little time or budget, when you have access to relevant populations, when you only want to get quick feedback from a few people, when you can spend 20 minutes or less per participant, when you have a product related to a specific physical space (e.g., an art museum tour application).

How To Do It

Find a location, get permission if needed, create a script. I’ve previously written a detailed article on the specifics of recruiting participants in public.

Positive Aspects

Quick execution, the potential for multiple locations if you have the resources, small or large sample sizes, accessing relevant populations, compatible with multiple research methods.

Negative Aspects

Little ability for screening, approaching people takes practice and skill, potentially inclement weather if outside, a lot of standing around.

Incentives

I’d base the incentive on the amount of time and type of activity. For example, I might give a product discount code for something taking a minute or less. A $5 gift card if you are taking a few minutes of their time.

Case Study

I worked on a project examining the use of technology in library settings. Specifically, we wanted to understand the usability of a system for finding and locating materials within the library. We wanted to work with people who use a library. We needed to test inside of the library because the last part of testing involved physically locating the material.

We sent two researchers to spend multiple days at the library while it was open for patrons. We stood with clipboards at the entrance of the library. We asked patrons if they would spend a few minutes with us participating in our study. We then observed them using the system to search for an item and asked them to locate the material based on where the system told them it should be located.

Our biggest challenge was long periods of time where there were no new patrons coming into the library. We wanted to complete 30 to 40 sessions using three different scenarios. We had budgeted to spend one week onsite to get this many responses. We had to extend our timeline for the following week to reach our goal.

We were able to suggest improvements in the interface, terminology, and an explanation of where materials were located. We would not have had similar findings if we hadn’t been on location at a library and we might not have had as valuable insights if we used people who were not library patrons.

Friends And Family (Low On Time And Budget)

Sometimes, you might have very little opportunity to engage in research. There are many reasons for this, time, budget, or your working for a client who refuses to allow research as part of the project plan. The designers I’ve worked with still want to have some type of feedback to shape their thinking. You can still look to gather some meaningful data from those you have closest access to. Perhaps you are on a project where you are working on a product that is relevant to your coworkers or friends you have easy access to. You might ask a few of them to participate in interviews about the product.

Friends and family are the definition of a convenience sample, and should only be used when no other options exist. This is the most biased and least rigorous way of collecting data. However, you can still benefit from insights into experiences you might otherwise not get. You can use friends and family to participate in interviews or usability testing as a means of accessing informing your design. I strongly recommend conducting additional research, using one of the other methods of finding participants, as your design progresses.

When To Use It

As a last resort, when you have no budget, little time, yet you want to know something about the context or users you are designing for when you have access to relevant people to participate in the study. Background information of your participants.

How To Do It

Reach out to others you and your team know; you can include social media to distribute the call to participate, schedule a time to speak or send an email explaining what you’re asking participants to do (you can also distribute survey links this way)
Positive – you will get some feedback, almost instant, low budget

Negative Aspects

Most limited pool of participants, possibly less reach, you’re are relying on favors, less ability to screen for specific characteristics, introducing a larger bias due to familiarity with participants.

Incentives

I would incentivize based on time and budget. A $25 gift card is much less expensive than what you’d pay for a participant from a recruitment firm, but friends and family might find this amount acceptable for up to an hour of time.

Case Study

I was part of a project team responding to a (paid) request for proposals (RFP) from a major vacation industry company. We had two weeks to turn around our response, including design concepts to show our thinking. Most of our team had no experience in using the services from this specific industry. We needed to find out more information to help inform our response. We didn’t have the resources to undertake our typical research process of finding and interviewing stakeholders or representative end users. Instead, we reached out to friends and family members who stated they’d had experience in this vacation activity within the past three years.

We emailed our staff and asked if anyone had friends or family members with this qualification who’d be willing to engage in brief phone conversations about their experience. We conducted interviews with seven people over the course of the next two days. Our designers were able to use the insights we gained to better understand the types of needs users might have while vacationing. Our concepts attempted to address some of the issues our participants stated existed when they had experienced while vacationing.

Although we didn’t win the long-term work, our team was able to place among the top candidates. We credited the participation of friends and family in our research as part of what helped our design stand out in a positive way. We were later awarded separate work from the team we presented to for the initial RFP.

The table below provides a summary of key characteristics for each participant recruitment method I’ve covered in this article.

Time Cost Ability to pre-screen participants Ability to access participants
Stakeholder Slow Low Easy Easy
Client Recruits Slow Low Difficult Difficult
Recruitment Firm Slow High Easy Varies – harder to reach specific populations
Guerilla Recruiting Fast Free Difficult Easy
Friends & Family Fast Free Moderate Easy depending on topic

Table 1: Characteristics of common research participant recruitment methods

Conclusion

We need to access users and potential users in order to effectively conduct research. I’ve covered a number of common ways you can find research participants. Each has certain strengths and weaknesses. You’ll want to become familiar with each of these and adapt your approach based on your product, budget, and timeline.

Smashing Editorial
(cc, ra, il)


Source:

Finding UX Research Participants