Tag Archives: css

Thumbnail

The Holy Grail Of Reusable Components: Custom Elements, Shadow DOM, And NPM




The Holy Grail Of Reusable Components: Custom Elements, Shadow DOM, And NPM

Oliver Williams



For even the simplest of components, the cost in human-labour may have been significant. UX teams do usability testing. An array of stakeholders have to sign off on the design.

Developers conduct AB tests, accessibility audits, unit tests and cross-browser checks. Once you’ve solved a problem, you don’t want to repeat that effort. By building a reusable component library (rather than building everything from scratch), we can continuously utilize past efforts and avoid revisiting already solved design and development challenges.


A screenshot of Google’s material components website – showing various components.


Large preview

Building an arsenal of components is particularly useful for companies such as Google that own a considerable portfolio of websites all sharing a common brand. By codifying their UI into composable widgets, larger companies can both speed up development time and achieve consistency of both visual and user-interaction design across projects. There’s been a rise in interest in style guides and pattern libraries over the last several years. Given multiple developers and designers spread over multiple teams, large companies seek to attain consistency. We can do better than simple color swatches. What we need is easily distributable code.

Sharing And Reusing Code

Manually copy-and-pasting code is effortless. Keeping that code up-to-date, however, is a maintenance nightmare. Many developers, therefore, rely on a package manager to reuse code across projects. Despite its name, the Node Package Manager has become the unrivalled platform for front-end package management. There are currently over 700,000 packages in the NPM registry and billions of packages are downloaded every month. Any folder with a package.json file can be uploaded to NPM as a shareable package. While NPM is primarily associated with JavaScript, a package can include CSS and markup. NPM makes it easy to reuse and, importantly, update code. Rather than needing to amend code in myriad places, you change the code only in the package.

The Markup Problem

Sass and Javascript are easily portable with the use of import statements. Templating languages give HTML the same ability — templates can import other fragments of HTML in the form of partials. You can write the markup for your footer, for example, just once, then include it in other templates. To say there exists a multiplicity of templating languages would be an understatement. Tying yourself to just one severely limits the potential reusability of your code. The alternative is to copy-and-paste markup and to use NPM only for styles and javascript.

This is the approach taken by the Financial Times with their Origami component library. In her talk “Can’t You Just Make It More like Bootstrap?” Alice Bartlett concluded “there is no good way to let people include templates in their projects”. Speaking about his experience of maintaining a component library at Lonely Planet, Ian Feather reiterated the problems with this approach:

“Once they copy that code they are essentially cutting a version which needs to be maintained indefinitely. When they copied the markup for a working component it had an implicit link to a snapshot of the CSS at that point. If you then update the template or refactor the CSS, you need to update all versions of the template scattered around your site.”

A Solution: Web Components

Web components solve this problem by defining markup in JavaScript. The author of a component is free to alter markup, CSS, and Javascript. The consumer of the component can benefit from these upgrades without needing to trawl through a project altering code by hand. Syncing with the latest changes project-wide can be achieved with a terse npm update via terminal. Only the name of the component and its API need to stay consistent.

Installing a web component is as simple as typing npm install component-name into a terminal. The Javascript can be included with an import statement:

<script type="module">
import './node_modules/component-name/index.js';
</script>

Then you can use the component anywhere in your markup. Here is a simple example component that copies text to the clipboard.

See the Pen Simple web component demo by CSS GRID (@cssgrid) on CodePen.

A component-centric approach to front-end development has become ubiquitous, ushered in by Facebook’s React framework. Inevitably, given the pervasiveness of frameworks in modern front-end workflows, a number of companies have built component libraries using their framework of choice. Those components are reusable only within that particular framework.


A component from IBM’s Carbon Design System


A component from IBM’s Carbon Design System. For use in React applications only. Other significant examples of component libraries built in React include Atlaskit from Atlassian and Polaris from Shopify. (Large preview)

It’s rare for a sizeable company to have a uniform front-end and replatorming from one framework to another isn’t uncommon. Frameworks come and go. To enable the maximum amount of potential reuse across projects, we need components that are framework agnostic.


A screenshot from npmjs.com showing components that do that same thing built exclusively for particular javascript frameworks.


Searching for components via npmjs.com reveals a fragmented Javascript ecosystem. (Large preview)


A graph charting the popularity of frameworks over time. Ember, Knockout and Backbone have plunged in popularity, replaced by newer offerings.


The ever-changing popularity of frameworks over time. (Large preview)

“I have built web applications using: Dojo, Mootools, Prototype, jQuery, Backbone, Thorax, and React over the years…I would love to have been able to bring that killer Dojo component that I slaved over with me to my React app of today.”

Dion Almaer, Director of Engineering, Google

When we talk about a web component, we are talking about the combination of a custom element with shadow DOM. Custom Elements and shadow DOM are part of both the W3C DOM specification and the WHATWG DOM Standard — meaning web components are a web standard. Custom elements and shadow DOM are finally set to achieve cross-browser support this year. By using a standard part of the native web platform, we ensure that our components can survive the fast-moving cycle of front-end restructuring and architectural rethinks. Web components can be used with any templating language and any front-end framework — they’re truly cross-compatible and interoperable. They can be used everywhere from a WordPress blog to a single page application.


The Custom Elements Everywhere project by Rob Dodson documents the interoperability of web components with various client-side Javascript frameworks.


The Custom Elements Everywhere project by Rob Dodson documents the interoperability of web components with various client-side Javascript frameworks. React, the outlier here, will hopefully resolve these issues with React 17. (Large preview)

Making A Web Component

Defining A Custom Element

It’s always been possible to make up tag-names and have their content appear on the page.

<made-up-tag>Hello World!</made-up-tag>

HTML is designed to be fault tolerant. The above will render, even though it’s not a valid HTML element. There’s never been a good reason to do this — deviating from standardized tags has traditionally been a bad practice. By defining a new tag using the custom element API, however, we can augment HTML with reusable elements that have built-in functionality. Creating a custom element is much like creating a component in React — but here were extending HTMLElement.

class ExpandableBox extends HTMLElement 
      constructor() 
        super()
      
    }

A parameter-less call to super() must be the first statement in the constructor. The constructor should be used to set up initial state and default values and to set up any event listeners. A new custom element needs to be defined with a name for its HTML tag and the elements corresponding class:

customElements.define('expandable-box', ExpandableBox)

It’s a convention to capitalize class names. The syntax of the HTML tag is, however, more than a convention. What if browsers wanted to implement a new HTML element and they wanted to call it expandable-box? To prevent naming collisions, no new standardized HTML tags will include a dash. By contrast, the names of custom elements have to include a dash.

customElements.define('whatever', Whatever) // invalid
customElements.define('what-ever', Whatever) // valid

Custom Element Lifecycle

The API offers four custom element reactions — functions that can be defined within the class that will automatically be called in response to certain events in the lifecycle of a custom element.

connectedCallback is run when the custom element is added to the DOM.

connectedCallback() 
    console.log("custom element is on the page!")

This includes adding an element with Javascript:

document.body.appendChild(document.createElement("expandable-box")) //“custom element is on the page”

as well as simply including the element within the page with a HTML tag:

<expandable-box></expandable-box> // "custom element is on the page"

Any work that involves fetching resources or rendering should be in here.

disconnectedCallback is run when the custom element is removed from the DOM.

disconnectedCallback() 
    console.log("element has been removed")

document.querySelector("expandable-box").remove() //"element has been removed"

adoptedCallback is run when the custom element is adopted into a new document. You probably don’t need to worry about this one too often.

attributeChangedCallback is run when an attribute is added, changed, or removed. It can be used to listen for changes to both standardized native attributes like disabled or src, as well as any custom ones we make up. This is one of the most powerful aspects of custom elements as it enables the creation of a user-friendly API.

Custom Element Attributes

There are a great many HTML attributes. So that the browser doesn’t waste time calling our attributeChangedCallback when any attribute is changed, we need to provide a list of the attribute changes we want to listen for. For this example, we’re only interested in one.

static get observedAttributes() 
            return ['expanded']
        

So now our attributeChangedCallback will only be called when we change the value of the expanded attribute on the custom element, as it’s the only attribute we’ve listed.

HTML attributes can have corresponding values (think href, src, alt, value etc) while others are either true or false (e.g. disabled, selected, required). For an attribute with a corresponding value, we would include the following within the custom element’s class definition.

get yourCustomAttributeName() 
  return this.getAttribute('yourCustomAttributeName');

set yourCustomAttributeName(newValue) 
  this.setAttribute('yourCustomAttributeName', newValue);

For our example element, the attribute will either be true or false, so defining the getter and setter is a little different.

get expanded() 
  return this.hasAttribute('expanded')

        
// the second argument for setAttribute is mandatory, so we’ll use an empty string
set expanded(val) 
  if (val) 
    this.setAttribute('expanded', '');
  
  else 
    this.removeAttribute('expanded')
  
}

Now that the boilerplate has been dealt with, we can make use of attributeChangedCallback.

attributeChangedCallback(name, oldval, newval) 
  console.log(`the $name attribute has changed from $oldval to $newval!!`);
  // do something every time the attribute changes
}

Traditionally, configuring a Javascript component would have involved passing arguments to an init function. By utilising the attributeChangedCallback, its possible to make a custom element that’s configurable just with markup.

Shadow DOM and custom elements can be used separately, and you may find custom elements useful all by themselves. Unlike shadow DOM, they can be polyfilled. However, the two specs work well in conjunction.

Attaching Markup And Styles With Shadow DOM

So far, we’ve handled the behavior of a custom element. In regard to markup and styles, however, our custom element is equivalent to an empty unstyled <span>. To encapsulate HTML and CSS as part of the component, we need to attach a shadow DOM. It’s best to do this within the constructor function.

class FancyComponent extends HTMLElement 
        constructor() 
            super()
            var shadowRoot = this.attachShadow(mode: 'open')
            shadowRoot.innerHTML = `<h2>hello world!</h2>`
            }

Don’t worry about understanding what the mode means — its boilerplate you have to include, but you’ll pretty much always want open. This simple example component will just render the text “hello world”. Like most other HTML elements, a custom element can have children — but not by default. So far the above custom element we’ve defined won’t render any children to the screen. To display any content between the tags, we need to make use of a slot element.

shadowRoot.innerHTML = `
<h2>hello world!</h2>
<slot></slot>
`

We can use a style tag to apply some CSS to the component.

shadowRoot.innerHTML = 
`<style>
p 
color: red;

</style>
<h2>hello world!</h2>
<slot>some default content</slot>`

These styles will only apply to the component, so we are free to make use of element selectors without the styles affecting anything else of the page. This simplifies writing CSS, making naming conventions like BEM unnecessary.

Publishing A Component On NPM

NPM packages are published via the command line. Open a terminal window and move into a directory that you would like to turn into a reusable package. Then type the following commands into the terminal:

  1. If your project doesn’t already have a package.json, npm init will walk you through generating one.
  2. npm adduser links your machine to your NPM account. If you don’t have a preexisting account, it will create a new one for you.
  3. npm publish

NPM packages are published via the command line


Large preview

If all’s gone well, you now have a component in the NPM registry, ready to be installed and used in your own projects — and shared with the world.


An example of a component in the NPM registry, ready to be installed and used in your own projects.


Large preview

The web components API isn’t perfect. Custom elements are currently unable to include data in form submissions. The progressive enhancement story isn’t great. Dealing with accessibility isn’t as easy as it should be.

Although originally announced in 2011, browser support still isn’t universal. Firefox support is due later this year. Nevertheless, some high-profile websites (like Youtube) are already making use of them. Despite their current shortcomings, for universally shareable components they’re the singular option and in the future we can expect exciting additions to what they have to offer.

Smashing Editorial
(il, ra, yk)


See the original article here:

The Holy Grail Of Reusable Components: Custom Elements, Shadow DOM, And NPM

Advanced CSS Layouts With Flexbox and CSS Grid

Full-day workshop • June 28th
This workshop is designed for designers and developers who already have a good working knowledge of HTML and CSS. We will cover a range of CSS methods for achieving layout, from those you are safe to use right now even if you need to support older version of Internet Explorer through to things that while still classed as experimental, are likely to ship in browsers in the coming months.

Link – 

Advanced CSS Layouts With Flexbox and CSS Grid

New Front-End Adventures in Responsive Design

Full-day workshop — June 28th With HTTP/2, Service Workers, Responsive Images, Flexbox, CSS Grid, SVG, WAI-ARIA roles and Font Loading API now available in browsers, we all are still trying to figure out just the right strategy for designing and buildings responsive websites efficiently. We want to use all of these technologies and smart processes like atomic design, but how can we use them efficiently, and how do we achieve it within a reasonable amount of time?

Read More:

New Front-End Adventures in Responsive Design

Intro to Vue.js

Full-day workshop — June 25th
Vue.js brings together the best features of the javascript framework landscape elegantly. If you’re interested in writing maintainable, clean code in an exciting and expressive manner, you should consider joining this class.
We’ll run through all the ways that Vue quickly solves common Frontend developer implementations, as well flexible ways to access the underlying API so that you can solve for any use-case.
Vue is uniquely friendly to all levels of web developers.

View original: 

Intro to Vue.js

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

Monthly Web Development Update 6/2018: Complexity, DNS Over HTTPS, And Push Notifications




Monthly Web Development Update 6/2018: Complexity, DNS Over HTTPS, And Push Notifications

Anselm Hannemann



We see complexity in every corner of a web project these days. We’ve read quite a bunch of articles about how complex a specific technology has become, and we discuss this over and over again. Coming from a time where we uploaded websites via FTP and had no git or anything comparable, now living in a time where we have a build system, transpilers, frameworks, tests, and a CI even for the smallest projects, this is easy to understand. But on the other hand, web development has grown up so much in the past 15 years that we can’t really compare today to the past anymore. And while it might seem that some things were easier in the past, we neglect the advantages and countless possibilities we have today. When we didn’t write tests back then, well, we simply had no test — meaning no reliable way to test for success. When we had no deployment process, it was easy to upload a new version but just as easy to break something — and it happened a lot more than today when a Continuous Integration system is in place.

Jeffrey Zeldman wrote an interesting article on the matter: “The Cult of Complex” outlines how we lose ourselves in unnecessary details and often try to overthink problems. I like the challenge of building systems that are not too complex but show a decent amount of responsibility (when it comes to ethics, privacy, security, a great user experience, and performance) and are working reliably (tests, deployments, availability, and performance again). I guess the problem of finding the right balance won’t go away anytime soon. Complexity is everywhere — we just need to decide if it’s useful complexity or if it was added simply because it was easier or because we were over-engineering the original problem.

News

  • The upcoming Safari version 12 was unveiled at Apple’s WWDC. Here’s what’s new: icons in tabs, strong passwords, as well as a password generator control via HTML attributes including two-factor authentication control, a 3D and AR model viewer, the Fullscreen API on iPads, font-display, and, very important, Intelligent Tracking Prevention 2.0 which is more restrictive than ever and might have a significant impact on the functionality of existing websites.
  • The headless Chrome automation library Puppeteer is now out in version 1.5. It brings along Browser contexts to isolate cookies and other data usually shared between pages, and Workers can now be used to interact with Web Workers, too.
  • Google released Lighthouse 3.0, the third major version of their performance analyzation tool which features a new report interface, some scoring changes, a CSV export, and First Contentful Paint measurement.
  • Chrome 67 is here, bringing Progressive Web Apps to the Desktop, as well as support for the Generic Sensor API, and extending the Credential Management API to support U2F authenticators via USB.
  • We’ve seen quite some changes in the browsers’ security interfaces over the past months. First, they emphasized sites that offer a secured connection (HTTPS). Then they decided to indicate insecure sites, and now Chrome announced new changes coming in fall that will make HTTPS the default by marking HTTP pages as “not secure”.
Desktop PWA in Chrome 67
Desktop Progressive Web Apps are now supported in Chrome OS 67, and the Chrome team already started working on support for Mac and Windows, too. (Image credit)

General

  • In “The Cult of the Complex”, Jeffrey Zeldman writes about how we often seem to forget that simplicity is the key and goal of everything we do, the overall goal for projects and life. He explains why it’s so hard to achieve and why it’s so much easier — and tempting — to cultivate complex systems. A very good read and definitely a piece I’ll add to my ‘evergreen’ list.
  • Heydon Pickering shared a new, very interesting article that teaches us to build a web component properly: This time he explains how to build an inclusive and responsive “Card” module.

UI/UX

  • Cool Backgrounds is a cool side project by Moe Amaya. It’s an online generator for polygonal backgrounds with gradients that can generate a lot of variants and shapes. Simply beautiful.

Tooling

Security

  • As security attacks via DNS gain popularity, DNS over HTTPS gets more and more important. Lin Clark explains the technology with a cartoon to make it easier to understand.
  • Windows Edge is now previewing support for same-site cookies. The attribute to lock down cookies even more is already available in Firefox and Chrome, so Safari is the only major browser that still needs to implement it, but I guess it’ll land in their Tech Preview builds very soon as well.
DNS Over HTTPS
Lin Clark created a cartoon to explain how you can better protect your users’ privacy with DNS over HTTPS. (Image credit)

Privacy

Web Performance

  • KeyCDN asked 15 people who know a lot about web performance to share their best advice with readers. Now they shared this article containing a lot of useful performance tips for 2018, including a few words by myself.
  • Stefan Judis discovered that we can already preload ECMA Script modules in Chrome 66 by adding an HTML header tag link rel=“modulepreload”.

Accessibility

  • It’s relatively easy to build a loading spinner — for a Single Page Application during load, for example —, but we rarely think about making them accessible. Stuart Nelson now explains how to do it.
  • Paul Stanton shares which accessibility tools we should use to get the best results.

JavaScript

  • JavaScript has lately been bullied by people who favor Elm, Rust, TypeScript, Babel or Dart. But JavaScript is definitely not worse, as Andrea Giammarchi explains with great examples. This article is also a great read for everyone who uses one of these other languages as it shows a couple of pitfalls that we should be aware of.
  • For a lot of projects, we want to use analytics or other scripts that collect personal information. With GDPR in effect, this got a lot harder. Yett is a nice JavaScript tool that lets you block the execution of such resources until a user agrees to it.
  • Ryan Miller created a new publication called “The Frontendian”, and it features one of the best explanations and guides to CORS I’ve come across so far.
  • The folks at Microsoft created a nice interactive demo page to show what Web Push Notifications can and should look like. If you haven’t gotten to grips with the technology yet, it’s a great primer to how it all works and how to build an interface that doesn’t disturb users.
  • Filepond is a JavaScript library for uploading files. It looks great and comes with a lot of adapters for React, Vue, Angular, and jQuery.
  • React 16.4 is out and brings quite a feature to the library: Pointer Events. They’ll make it easier to deal with user interactions and have been requested for a long time already.
The Frontendian
Inspired by the parallels between basic astrological ideas and push notification architecture, the team at Microsoft explains how to send push notifications to a user without needing the browser or app to be opened. (Image credit)

CSS

Work & Life

  • Anton Sten wrote about the moral implications for our apps. A meaningful explanation why the times of “move fast and break things” are definitely over as we’re dealing with Artificial Intelligence, social networks that affect peoples’ lives, and privacy matters enforced by GDPR.
  • Basecamp now has a new chart type to display a project’s status: the so-called “hill chart” adds a better context than a simple progress bar could ever do it.
  • Ben Werdmüller shares his thoughts about resumes and how they always fail to reflect who you are, what you do, and why you should be hired.

I hope you enjoyed this monthly update. The next one is scheduled for July 13th, so stay tuned. In the meantime, if you like what I do, please consider helping me fund the Web Development Reading List financially.

Have a great day!

— Anselm

Smashing Editorial
(cm)


View post:  

Monthly Web Development Update 6/2018: Complexity, DNS Over HTTPS, And Push Notifications

Thumbnail

Meet Smashing Book 6: New Frontiers In Web Design




Meet Smashing Book 6: New Frontiers In Web Design

Vitaly Friedman



Let’s make sense of the front-end and UX madness. Meet Smashing Book 6 with everything from design systems to accessible single-page apps, CSS Custom Properties, Grid, Service Workers, performance patterns, AR/VR, conversational UIs & responsive art direction. And you can add your name into the book, too. About the book ↓.

Smashing Book 6 is dedicated to the challenges and headaches that we are facing today, and how to resolve them. No chit-chat, no theory: only practical, useful advice applicable to your work right away.

Smashing Book 6

Book

$29 $39Print + eBook

Printed, quality hardcover. Free airmail shipping worldwide. Sep 2018.

eBook

$14.90 $19Get the eBook

PDF, ePUB, Kindle. First chapters are already available.

About The Book

With so much happening in front-end and UX these days, it’s getting really hard to stay afloat, isn’t it? New technologies and techniques appear almost daily, and so navigating through it all in regular projects is daunting and time-consuming. Frankly, we just don’t have time to afford betting on a wrong strategy. That’s why we created Smashing Book 6, our shiny new book that explores uncharted territories and seeks to establish a map for the brave new front-end world.

You know the drill: the book isn’t about tools; it’s about workflow, strategy and shortcuts to getting things done well. Respected members of the community explore how to build accessible single-page apps with React or Angular, how to use CSS Grid Layout, CSS Custom Properties and service workers as well as how to load assets on the web in times of HTTP/2 and bloated third-party scripts.

We’ll also examine how to make design systems work in real-life, how to design and build conversational interfaces, AR/VR, building for chatbots and watches and how to bring responsive art-direction back to the web.

Print shipping in late September 2018., hardcover + eBook. 432 pages. Written by Laura Elizabeth, Marcy Sutton, Rachel Andrew, Mike Riethmueller, Harry Roberts, Lyza D. Gardner, Yoav Weiss, Adrian Zumbrunnen, Greg Nudelman, Ada Rose Cannon and Vitaly Friedman. Pre-order the book today.

A look inside the book

Book

$29 $39Print + eBook

Printed, quality hardcover. Free airmail shipping worldwide. Shipping: Sept 2018.

eBook

$14.90 $19Get the eBook

PDF, ePUB, Kindle. First chapters are already available.

Why This Book Is For You

We worked hard to design the book in a way that it doesn’t become outdated quickly. That’s why it’s more focused on strategy rather than tooling. It’s about how we do things, but not necessarily the tools we all use to get there.

Table of Contents

The book contains 11 chapters, with topics ranging from design to front-end. Only practical advice, applicable to your work right away.

  • Making design systems work in real-life
    by Laura Elizabeth
  • Accessibility in times of SPAs
    by Marcy Sutton
  • Production-ready CSS Grid layouts
    by Rachel Andrew
  • Strategic guide to CSS Custom Properties
    by Mike Riethmueller
  • Taming performance bottlenecks
    by Harry Roberts
  • Building an advanced service worker
    by Lyza Gardner
  • Loading assets on the web
    by Yoav Weiss
  • Conversation interface design patterns
    by Adrian Zumbrunnen
  • Building chatbots and designing for watches
    by Greg Nudelman
  • Cross Reality and the web (AR/VR)
    by Ada Rose Cannon
  • Bringing personality back to the web
    by Vitaly Friedman
laura elizabeth
marcy sutton
rachel andrew
mike riethmuller
harry roberts
lyza d gardner
yoav weiss
adrian zumbrunnen
greg nudelman
ada rose edwards
vitaly friedman

From left to right: Laura Elizabeth, Marcy Sutton, Rachel Andrew, Mike Riethmuller, Harry Roberts, Lyza Gardner, Yoav Weiss, Adrian Zumbrunnen, Greg Nudelman, Ada Rose Edwards, and yours truly.

Book

$29 $39Print + eBook

Free airmail shipping worldwide. Sep 2018. Pre-order now and save 25%

eBook

$14.90 $19Get the eBook

PDF, ePUB, Kindle. You can start reading right away.

Download the Sample Chapter

To sneak a peek inside the book, you can download Vitaly’s chapter on bringing personality back to the web (PDF, ca. 18MB). Enjoy!

Goodie: New Frontiers In Web Design Wallpaper

To celebrate the pre-release of the book , Chiara Aliotta designed a set of mobile and desktop wallpapers for you to indulge in. Feel free to download it (ZIP, ca. 1.4MB).

New Frontiers In Web Design Wallpaper
Get ready for the new frontiers in web design with Chiara’s beautiful wallpaper.

About The Designer

Chiara AliottaThe cover was designed by the fantastic Chiara Aliotta. Chiara is an Italian award-winning designer with many years of experience as an art director and brand consultant. She founded the design studio Until Sunday and has directed the overall artistic look and feel of different tech companies and not-for-profit organizations around the world. We’re very happy that she gave Smashing Book 6 that special, magical touch.

Add Your Name To The Book

We kindly appreciate your support, and so as always, we invite you to add your name to the printed book: a double-page spread is reserved for the first 1.000 people. Space is limited, so please don’t wait too long!

Add your name to the book
A double-page spread is reserved for the first 1.000 readers. Add your name to the printed book.

Book

$29 $39Print + eBook

Free airmail shipping worldwide. Sep 2018. Pre-order now and save 25%

eBook

$14.90 $19Get the eBook

PDF, ePUB, Kindle. You can start reading right away.

Smashing Editorial
(cm)


Originally posted here:

Meet Smashing Book 6: New Frontiers In Web Design

Thumbnail

Creating The Feature Queries Manager DevTools Extension




Creating The Feature Queries Manager DevTools Extension

Ire Aderinokun



Within the past couple of years, several game-changing CSS features have been rolled out to the major browsers. CSS Grid Layout, for example, went from 0 to 80% global support within the span of a few months, making it an incredibly useful and reliable tool in our arsenal. Even though the current support for a feature like CSS Grid Layout is relatively great, not all recent or current browsers support it. This means it’s very likely that you and I will currently be developing for a browser in which it is not supported.

The modern solution to developing for both modern and legacy browsers is feature queries. They allow us to write CSS that is conditional on browser support for a particular feature. Although working with feature queries is almost magical, testing them can be a pain. Unlike media queries, we can’t easily simulate the different states by just resizing the browser. That’s where the Feature Queries Manager comes in, an extension to DevTools to help you easily toggle your feature query conditions. In this article, I will cover how I built this extension, as well as give an introduction to how developer tools extensions are built.

Working With Unsupported CSS

If a property-value pair (e.g. display: grid), is not supported by the browser the page is viewed in, not much happens. Unlike other programming languages, if something is broken or unsupported in CSS, it only affects the broken or unsupported rule, leaving everything else around it intact.

Let’s take, for example, this simple layout:

The layout in a supporting browser


Large preview

We have a header spanning across the top of the page, a main section directly below that to the left, a sidebar to the right, and a footer spanning across the bottom of the page.

Here’s how we could create this layout using CSS Grid:

See the Pen layout-grid by Ire Aderinokun (@ire) on CodePen.

In a supporting browser like Chrome, this works just as we want. But if we were to view this same page in a browser that doesn’t support CSS Grid Layout, this is what we would get:

The layout in an unsupporting browser


Large preview

It is essentially the same as if we had not applied any of the grid-related styles in the first place. This behavior of CSS was always intentional. In the CSS specification, it says:

In some cases, user agents must ignore part of an illegal style sheet, [which means to act] as if it had not been there

Historically, the best way to handle this has been to make use of the cascading nature of CSS. According to the specification, “the last declaration in document order wins.” This means that if there are multiple of the same property being defined within a single declaration block, the latter prevails.

For example, if we have the follow declarations:

body 
  display: flex;
  display: grid;

Assuming both Flexbox and Grid are supported in the browser, the latter — display: grid — will prevail. But if Grid is not supported by the browser, then that rule is ignored, and any previous valid and supported rules, in this case display: flex, are used instead.

body 
  display: flex;
  display: grid;

Cascading Problems

Using the cascade as a method for progressive enhancement is and has always been incredibly useful. Even today, there is no simpler or better way to handle simple one-liner fallbacks, such as this one for applying a solid colour where the rgba() syntax is not supported.

div 
    background-color: rgb(0,0,0);
    background-color: rgba(0,0,0,0.5);

Using the cascade, however, has one major limitation, which comes into play when we have multiple, dependent CSS rules. Let’s again take the layout example. If we were to attempt to use this cascade technique to create a fallback, we would end up with competing CSS rules.

See the Pen layout-both by Ire Aderinokun (@ire) on CodePen.

In the fallback solution, we need to use certain properties such as margins and widths, that aren’t needed and in fact interfere with the “enhanced” Grid version. This makes it difficult to rely on the cascade for more complex progressive enhancement.

Feature Queries To The Rescue!

Feature queries solve the problem of needing to apply groups of styles that are dependent on the support of a CSS feature. Feature queries are a “nested at-rule” which, like the media queries we are used to, allow us to create a subset of CSS declarations that are applied based on a condition. Unlike media queries, whose condition is dependent on device and screen specs, feature query conditions are instead based on if the browser supports a given property-value pair.

A feature query is made up of three parts:

  1. The @supports keyword
  2. The condition, e.g. display: flex
  3. The nested CSS declarations.

Here is how it looks:

@supports (display: grid) 
    body  display: grid; 
}

If the browser supports display: grid, then the nested styles will apply. If the browser does not support display: grid, then the block is skipped over entirely.

The above is an example of a positive condition within a feature query, but there are four flavors of feature queries:

  1. Positive condition, e.g. @supports (display grid)

  2. Negative condition, e.g. @supports not (display: grid)

  3. Conjunction, e.g. @supports (display:flex) and (display: grid)

  4. Disjunction, e.g. @supports (display:-ms-grid) or (display: grid)

Feature queries solve the problem of having separate fallback and enhancement groups of styles. Let’s see how we can apply this to our example layout:

See the Pen Run bunny run by Ire Aderinokun (@ire) on CodePen.

Introducing The Feature Queries Manager

When we write media queries, we test them by resizing our browser so that the styles at each breakpoint apply. So how do we test feature queries?

Since feature queries are dependent on whether a browser supports a feature, there is no easy way to simulate the alternative state. Currently, the only way to do this would be to edit your code to invalidate/reverse the feature query.

For example, if we wanted to simulate a state in which CSS Grid is not supported, we would have to do something like this:

/* fallback styles here */

@supports (display: grrrrrrrrid) 
    /* enhancement styles here */

This is where the Feature Queries Manager comes in. It is a way to reverse your feature queries without ever having to manually edit your code.

(Large preview)

It works by simply negating the feature query as it is written. So the following feature query:

@supports (display: grid) 
    body  display: grid; 
}

Will become the following:

@supports not (display: grid) 
    body  display: grid; 
}

Fun fact, this method works for negative feature queries as well. For example, the following negative feature query:

@supports not (display: grid) 
    body  display: block; 
}

Will become the following:

@supports not (not (display: grid)) 
    body  display: block; 
}

Which is actually essentially the same as removing the “not” from the feature query.

@supports (display: grid) 
    body  display: block; 
}

Building The Feature Queries Manager

FQM is an extension to your browser’s Developer Tools. It works by registering all the CSS on a page, filtering out the CSS that is nested within a feature query, and giving us the ability to toggle the normal or “inverted” version of that feature query.

Creating A DevTools Panel

Before I go on to how I specifically built the FQM, let’s cover how to create a new DevTools panel in the first place. Like any other browser extension, we register a DevTools extension with the manifest file.


  "manifest_version": 2,
  "name": "Feature Queries Manager",
  "short_name": "FQM",
  "description": "Manage and toggle CSS on a page behind a @supports Feature Query.",
  "version": "0.1",
  "permissions": [
    "tabs",
    "activeTab",
    "<all_urls>"
  ],
  "icons": 
    "128": "images/icon@128.png",
    "64": "images/icon@64.png",
    "16": "images/icon@16.png",
    "48": "images/icon@48.png"
  
}

To create a new panel in DevTools, we need two files — a devtools_page, which is an HTML page with an attached script that registers the second file, panel.html, which controls the actual panel in DevTools.

The devtools script creates the panel page


Large preview

First, we add the devtools_page to our manifest file:


  "manifest_version": 2,
  "name": "Feature Queries Manager",
  ...
  "devtools_page": "devtools.html",

Then, in our devtools.html file, we create a new panel in DevTools:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"></head>
<body>
<!-- Note: I’m using the browser-polyfill to be able to use the Promise-based WebExtension API in Chrome -->
<script src="../browser-polyfill.js"></script>

<!-- Create FQM panel -->
<script>
browser.devtools.panels.create("FQM", "images/icon@64.png", "panel.html");
</script>
</body>
</html

Finally, we create our panel HTML page:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"></head>
<body>
  <h1>Hello, world!</h1>
</body>
</html>

If we open up our browser, we will see a new panel called “FQM” which loads the panel.html page.

A new panel in browser DevTools showing the “Hello, World” text


Large preview

Reading CSS From The Inspected Page

In the FQM, we need to access all the CSS referenced in the inspected document in order to know which are within feature queries. However, our DevTools panel doesn’t have direct access to anything on the page. If we want access to the inspected document, we need a content script.

The content script reads CSS from the HTML document


Large preview

A content script is a javascript file that has the same access to the html page as any other piece of javascript embedded within it. To register a content script, we just add it to our manifest file:


      "manifest_version": 2,
      "name": "Feature Queries Manager",
      ...
      "content_scripts": [
        "matches": [""],
        "js": ["browser-polyfill.js", "content.js"]
      ],
    }

In our content script, we can then read all the stylesheets and css within them by accessing document.styleSheets:

Array.from(document.styleSheets).forEach((stylesheet) => 
      let cssRules;
      
      try 
        cssRules = Array.from(stylesheet.cssRules);
       catch(err) 
        return console.warn(`[FQM] Can't read cssRules from stylesheet: $ stylesheet.href `);
      }
      
      cssRules.forEach((rule, i) => 
      
        /* Check if css rule is a Feature Query */
        if (rule instanceof CSSSupportsRule) 
          /* do something with the css rule */
        
        
      });
    });

Connecting The Panel And The Content Scripts

Once we have the rules from the content script, we want to send them over to the panel so they can be visible there. Ideally, we would want something like this:

The content script passes information to the panel and the panel sends instructions to modify CSS back to the content


Large preview

However, we can’t exactly do this, because the panel and content files can’t actually talk directly to each other. To pass information between these two files, we need a middleman — a background script. The resulting connection looks something like this:

The content and panel scripts communicate via a background script


Large preview

As always, to register a background script, we need to add it to our manifest file:


  "manifest_version": 2,
  "name": "Feature Queries Manager",
  ...
  "background": 
    "scripts": ["browser-polyfill.js", "background.js"]
  ,
}

The background file will need to open up a connection to the panel script and listens for messages coming from there. When the background file receives a message from the panel, it passes it on to the content script, which is listening for messages from the background. The background script waits for a response from the content script and relays that message back to the panel.

Here’s a basic of example of how that works:

// Open up a connection to the background script
const portToBackgroundScript = browser.runtime.connect();

// Send message to content (via background)
portToBackgroundScript.postMessage("Hello from panel!");

// Listen for messages from content (via background)
portToBackgroundScript.onMessage.addListener((msg) => 
  console.log(msg);
  // => "Hello from content!"
);
// backrgound.js

// Open up a connection to the panel script
browser.runtime.onConnect.addListener((port) => 
  
  // Listen for messages from panel
  port.onMessage.addListener((request) => 
  
    // Send message from panel.js -> content.js
    // and return response from content.js -> panel.js
    browser.tabs.sendMessage(request.tabId, request)
      .then((res) => port.postMessage(res));
  );
});
// content.js

// Listen for messages from background
browser.runtime.onMessage.addListener((msg) => 

  console.log(msg)
  // => "Hello from panel!"
  
  // Send message to panel
  return Promise.resolve("Hello from content!");
);

Managing Feature Queries

Lastly, we can get to the core of what the extension does, which is to “toggle” on/off the CSS related to a feature query.

If you recall, in the content script, we looped through all the CSS within feature queries. When we do this, we also need to save certain information about the CSS rule:

  1. The rule itself
  2. The stylesheet it belongs to
  3. The index of the rule within the stylesheet
  4. An “inverted” version of the rule.

This is what that looks like:

cssRules.forEach((rule, i) => 
  
  const cssRule = rule.cssText.substring(rule.cssText.indexOf(""));
  const invertedCSSText = `@supports not ( $ rule.conditionText  ) $ cssRule `;
  
  FEATURE_QUERY_DECLARATIONS.push( 
    rule: rule,
    stylesheet: stylesheet,
    index: i, 
    invertedCSSText: invertedCSSText
  );
  
});

When the content script receives a message from the panel to invert all declarations relating to the feature query condition, we can easily replace the current rule with the inverted one (or vice versa).

function toggleCondition(condition, toggleOn) 
  FEATURE_QUERY_DECLARATIONS.forEach((declaration) => 
    if (declaration.rule.conditionText === condition) 
      
      // Remove current rule
      declaration.stylesheet.deleteRule(declaration.index);
      
      // Replace at index with either original or inverted declaration
      const rule = toggleOn ? declaration.rule.cssText : declaration.invertedCSSText;
      declaration.stylesheet.insertRule(rule, declaration.index);
        
  });
}

And that is essentially it! The Feature Query Manager extension is currently available for Chrome and Firefox.

Limitations Of The FQM

The Feature Queries Manager works by “inverting” your feature queries, so that the opposite condition applies. This means that it cannot be used in every scenario.

Fallbacks

If your “enhancement” CSS is not written within a feature query, then the extension cannot be used as it is dependent on finding a CSS supports rule.

Unsupported Features

You need to take note of if the browser you are using the FQM in does or does not support the feature in question. This is particularly important if your original feature query is a negative condition, as inverting it will turn it into a positive condition. For example, if you wrote the following CSS:

div  background-color: blue; 

@supports not (display: grid) 
  div  background-color: pink; 
}

If you use the FQM to invert this condition, it will become the following:

div  background-color: blue; 

@supports (display: grid) 
  div  background-color: pink; 
}

For you to be able to actually see the difference, you would need to be using a browser which does in fact support display: grid.

I built the Feature Queries Manager as a way to more easily test the different CSS as I develop, but it isn’t a replacement for testing layout in the actual browsers and devices. Developer tools only go so far, nothing beats real device testing.

Smashing Editorial
(ra, yk, il)


Continued: 

Creating The Feature Queries Manager DevTools Extension

Thumbnail

Monthly Web Development Update 5/2018: Browser Performance, Iteration Zero, And Web Authentication




Monthly Web Development Update 5/2018: Browser Performance, Iteration Zero, And Web Authentication

Anselm Hannemann



As developers, we often talk about performance and request browsers to render things faster. But when they finally do, we demand even more performance.

Alex Russel from the Chrome team now shared some thoughts on developers abusing browser performance and explains why websites are still slow even though browsers reinvented themselves with incredibly fast rendering engines. This is in line with an article by Oliver Williams in which he states that we’re focusing on the wrong things, and instead of delivering the fastest solutions for slower machines and browsers, we’re serving even bigger bundles with polyfills and transpiled code to every browser.

It certainly isn’t easy to break out of this pattern and keep bundle size to a minimum in the interest of the user, but we have the technologies to achieve that. So let’s explore non-traditional ways and think about the actual user experience more often — before defining a project workflow instead of afterward.

Front-End Performance Checklist 2018

To help you cater for fast and smooth experiences, Vitaly Friedman summarized everything you need to know to optimize your site’s performance in one handy checklist. Read more →

News

General

  • Oliver Williams wrote about how important it is that we rethink how we’re building websites and implement “progressive enhancement” to make the web work great for everyone. After all, it’s us who make the experience worse for our users when blindly transpiling all our ECMAScript code or serving tons of JavaScript polyfills to those who already use slow machines and old software.
  • Ian Feather reveals that around 1% of all requests for JavaScript on BuzzFeed time out. That’s about 13 million requests per month. A good reminder of how important it is to provide a solid fallback, progressive enhancement, and workarounds.
  • The new GDPR (General Data Protection Regulation) directive is coming very soon, and while our inboxes are full of privacy policy updates, one thing that’s still very unclear is which services can already provide so-called DPAs (Data Processing Agreements). Joschi Kuphal collects services that offer a DPA, so that we can easily look them up and see how we can obtain a copy in order to continue using their services. You can help by contributing to this resource via Pull Requests.

UI/UX

Product design principles
How to create a consistent, harmonious user experience when designing product cards? Mei Zhang shares some valuable tips. (Image credit)

Security

Privacy

  • The GDPR Checklist is another helpful resource for people to check whether a website is compliant with the upcoming EU directive.
  • Bloomberg published a story about the open-source privacy-protection project pi-hole, why it exists and what it wants to achieve. I use the software daily to keep my entire home and work network tracking-free.
GDPR Compliance Checklist
Achieving GDPR Compliance shouldn’t be a struggle. The GDPR Compliance Checklist helps you see clearer. (Image credit)

Web Performance

  • Postgres 10 has been here for quite a while already, but I personally struggled to find good information on how to use all these amazing features it brings along. Gabriel Enslein now shares Postgres 10 performance updates in a slide deck, shedding light on how to use the built-in JSON support, native partitioning for large datasets, hash index resiliency, and more.
  • Andrew Betts found out that a lot of websites are using outdated headers. He now shares why we should drop old headers and which ones to serve instead.

Accessibility

Page previews
Page previews open possibilities in multiple areas, as Nirzar Pangarkar explains. (Image credit: Nirzar Pangarkar)

CSS

  • Rarely talked about for years, CSS tables are still used on most websites to show (and that’s totally the correct way to do so) data in tables. But as they’re not responsive by default, we always struggled when making them responsive and most of us used JavaScript to make them work on mobile screens. Lea Verou now found two new ways to achieve responsive tables by using CSS: One is to use text-shadow to copy text to other rows, the other one uses element() to copy the entire <thead> to other rows — I still try to understand how Lea found these solutions, but this is amazing!
  • Rachel Andrew wrote an article about building and providing print stylesheets in 2018 and why they matter a lot for users even if they don’t own a printer anymore.
  • Osvaldas Valutis shares how to implement the so-called “Priority Plus” navigation pattern mostly with CSS, at least in modern browsers. If you need to support older browsers, you will need to extend this solution further, but it’s a great start to implement such a pattern without too much JavaScript.
  • Rachel Andrew shares what’s coming up in the CSS Grid Level 2 and Subgrid specifications and explains what it is, what it can solve, and how to use it once it is available in browsers.

JavaScript

  • Chris Ashton “used the web for a day with JavaScript turned off.” This piece highlights the importance of thinking about possible JavaScript failures on websites and why it matters if you provide fallbacks or not.
  • Sam Thorogood shares how we can build a “native undo & redo for the web”, as used in many text editors, games, planning or graphical software and other occasions such as a drag and drop reordering. And while it’s not easy to build, the article explains the concepts and technical aspects to help us understand this complicated matter.
  • There’s a new way to implement element/container queries into your application: eqio is a tiny library using IntersectionObserver.

Work & Life

  • Johannes Seitz shares his thoughts about project management at the start of projects. He calls the method “Iteration Zero”. An interesting concept to understand the scope and risks of a project better at a time when you still don’t have enough experience with the project itself but need to build a roadmap to get things started.
  • Arestia Rosenberg shares why her number one advice for freelancers is to ‘lean into the moment’. It’s about doing work when you can and using your chance to do something else when you don’t feel you can work productively. In the end, the summary results in a happy life and more productivity. I’d personally extend this to all people who can do that, but, of course, it’s best applicable to freelancers indeed.
  • Sam Altman shares a couple of handy productivity tips that are not just a ‘ten things to do’ list but actually really helpful thoughts about how to think about being productive.

Going Beyond…

  • Ethan Marcotte elaborates on the ethical issues with Google Duplex that is designed to imitate human voice so well that people don’t notice if it’s a machine or a human being. While this sounds quite interesting from a technical point of view, it will push the debate about fake news much further and cause more struggle to differentiate between something a human said or a machine imitated.
  • Our world is actually built on promises, and here’s why it’s so important to stick to your promises even if it’s hard sometimes.
  • I bet that most of you haven’t heard of Palantir yet. The company is funded by Peter Thiel and is a data-mining company that has the intention to collect as much data as possible about everybody in the world. It’s known to collaborate with various law enforcement authorities and even has connections to military services. What they do with data and which data they have from us isn’t known. My only hope right now is that this company will suffer a lot from the EU GDPR directive and that the European Union will try to stop their uncontrolled data collection. Facebook’s data practices are nothing compared to Palantir it seems.
  • Researchers sound the alarm after an analysis showed that buying a new smartphone consumes as much energy as using an existing phone for an entire decade. I guess I’ll not replace my iPhone 7 anytime soon — it’s still an absolutely great device and just enough for what I do with it.
  • Anton Sten shares his thoughts on Vanity Metrics, a common way to share numbers and statistics out of context. And since he realized what relevancy they have, he thinks differently about most of the commonly readable data such as investments or usage data of services now. Reading one number without having a context to compare it to doesn’t matter at all. We should keep that in mind.

We hope you enjoyed this Web Development Update. The next one is scheduled for Friday, June 15th. Stay tuned.

Smashing Editorial
(cm)


See the original article here: 

Monthly Web Development Update 5/2018: Browser Performance, Iteration Zero, And Web Authentication

Thumbnail

Could the Breadcrumb Technique Help Boost Your Landing Page Conversions?

We’ve all heard the term “less is more”. And we’ve been told this applies for landing pages too. I.e. your forms should be short and only ask for only the bare minimum of required information if you want to convert.

However, when used across the board, this advice can backfire.

As an example, one of the main questions someone typically has when faced with a landing page is is how much your offer will cost. But if the offer on your landing page is for a free quote, you can’t necessarily disclose pricing on the page. When there’s no pricing, but instead a form requiring a name, phone number, and email, the visitor knows:

  • They’re going to need to talk to someone to get an answer to their question (they’re well aware you can’t give a customized quote from such limited info), plus, prospects are very reluctant to give their information out to just anyone.
  • They can click the back button and find a competitor that will give them what they want faster.

So why would we expect a form with super generic fields to be compelling enough for someone to engage with us in all cases?

As we’ve found at our agency KlientBoost, by increasing the amount of steps and the amount of form fields, we could actually increase conversion rates. The key here for us has been the order in which we present our steps and what info we ask for first.

Can more form fields really increase conversions?

As you may know, adding form fields goes against everything we’ve typically been advised to do:

You can find the sources for the above here, here, and here.

And while there are certainly cases in which fewer form fields are best, we’ve found adding more of the right form fields in progression can help ease conversion anxiety. When done correctly, it can take your free quote/lead generation landing pages to the next level.

At our agency we call our multi-step form approach the Breadcrumb Technique – think Hansel and Gretel where the breadcrumbs lead them in the right direction.

Experimenting with the Breadcrumb Technique

This is the landing page version of the sales technique called the “Yes Ladder”. It’s the art of eventually getting to what you want (the conversion) as a marketer, by getting visitors to say yes to much smaller requests first.

Click above to see a larger image of our landing page form flow. As each step progresses, the questions become more personal in nature.

Instead of having one page and one form to capture leads, you spread the form fields across two or more steps. So potential leads that visit the first page via your ads will fill in a short form and, after clicking the CTA button, they’re directed to the next step.

The first step starts with the least personal questions that allow the visitor to stay anonymous, whereas the second (and possible additional steps) ask for more, (albeit) reasonable, personal information. Here’s an example from one of our clients ZipLending. Their landing page offers a quote for rates on mortgages:

Notice the questions being asked in the step one form:

  • What kind of property are you considering?
  • What is your estimated credit score?
  • What is your desired loan amount?

All fairly low threat questions that allow the prospect to stay anonymous but feel like they’re going to get a quality answer they’re looking for, tailored to them.

Next, they’re directed to the second step form fields:

This step asks for more personal information, but logically reminds the prospect we need this information to send custom rates their way.

And while I can’t share the nitty gritty numbers of this test, I can share some high-level results. After the multi-step changes were made in the form above, we were able to bring in 35 more leads for ZipLending from March 2017 to May 2017. The client also noticed they were really high quality leads because of the qualifying questions we had included in our first step.

When we experimented with a multi-step form for another client, Garza Law, we were able to steadily increase the number of leads, bringing in 66 more in March 2018 than in December 2017, for example. Here’s a look at that:

Depending on the industry you’re working with and the typical value of a lead, 35-66 more leads in a given month can be a huge upgrade for a client and it’s why we’re thrilled to be able to deliver this via the multi-step form approach.

Why the BreadCrumb Technique is a cool experiment

If you want to try this with your landing pages, on the first step form, you set up questions pertinent to what the prospect might ask had they called you on the phone. This establishes the custom nature of what they will receive in return.

In the particular example we’ve outlined above, the visitor is interested in getting a no-obligation quote. So surely we’d need certain information on what they’re looking for to be helpful, and because the prospect understands this they’re more willing to participate for the perceived, increased value.

Replacing highly personal, red-flag-raising questions in the first step with questions that help the prospect hone in on exactly what they’re looking for will not only grow your conversions, but often improves lead quality as well.

Additionally, on the ZipLending page, notice the the headline changes between step one and two to let people know that they’re not yet finished with the process.


The “get rates” CTA button text also changes to “send rates”.
If the language does not differ from your step one to step two, this could cause a drop in conversions as people may think the form just refreshed and they’re done with the process.

Remember: all your landing page forms need to be GDPR compliant by May 25, 2018 (featuring privacy policies and opt-in checkboxes). Learn how to make your landing pages compliant by design here.

The psychology backing up this technique

After filling out the initial questions in step one, the last step of filling out the more sensitive fields like name, email, phone number becomes much easier because of compliance psychology.

Dr. Robert Cialdini said it best:

“Once we’ve made a choice, we will encounter personal and interpersonal pressures to behave consistently with that commitment.” Influence – The Psychology of Persuasion

In other words, once you commit to small things, you’re more likely to continue onto bigger commitments aligned with your initial decision.

Scott Fraser and Jonathan Freedman also conducted research on how to get people to say yes. They went door to door asking people to put up a sign that read: “Drive Carefully” in their front yard, but only 20% of people agreed to this.

They then did the same test in a nearby neighborhood, but this time they asked people to put much smaller signs in their yard. This created the opportunity to get them to eventually say yes to putting up the original, larger signs.

Next time around, 76% people agreed to put up the larger signs compared to the original 20%. Psychology baby!

Following the multi-step model designed to ease visitors into a commitment, here’s another successful built-in-Unbounce landing page example from one of our clients:

The first step

The first form step asks about what the prospect needs.

The second step

The second step, reminding the prospect that what they want is almost ready to go.
Notice how the first step asks for make, model, and year of the car. In this first step, make sure to ask questions that are super easy for the visitor to answer, but also strongly relate to your offer.

Successful multi-step forms weren’t a one-time thing for us

What’s cool is that this multi-step landing page technique has worked for us at KlientBoost several times for different clients.

Below you can see our client Mention’s Unbounce landing page offering their free demo, Auto Buyer’s landing page for their offer on your vehicle, and Watchex’s estimate for purchasing your Rolex. These campaigns all followed the same breadcrumb technique:

Client example: Mention.
Another client example: Auto Buyer’s.
Another client example: Watchex.

Progress bars can help light the way

When it comes to multi-step landing pages, something to consider testing is adding a progress bar, or a step wizard. This is especially handy when you have more than two steps, like the following example:

Step 1 says 0% complete.
Step 2 let’s the user know that this is the last step before completion.

The wizard signals to people just how much they will need to fill out, which can help ease any uncertainty about how much information is required.

In our experience, we’ve found it works best to include the wizard starting on the second step form fields and not the first. Visitors are more likely to continue through the whole process if they start the process, as per compliance psychology.

How do you try out The Breadcrumb Technique on your Unbounce landing pages?

It’s easy! Instead of having your usual one-step form, head to your form confirmation dialog and make your first-step’s form destination direct to the url of your second step (See below).

When you select the form in the Unbounce builder, you will see options on the right of where the form confirmation goes. Under confirmation, select “Go to URL”, then paste in the url of the second step form, and make sure that the “Append form data to URL” is checked.

For the second step of the form, you must make sure a very crucial step is completed, otherwise the information from your first step will not pass over and you will not receive a full lead. See below:

You will need to create hidden fields with the same field IDs of the form fields on your first step. If they don’t match, the information will not pass over. As long as you have all fields from the first step as hidden fields on the second step, you should be just fine.

Now that your first and second step are linked together correctly, you can continue with your regularly scheduled programming of sending the second step form to your form confirmation dialog (or a thank you page). All done!

Unbounce has an easy multi-step function

There’s always more than one way to do something! Although this requires some development work, Noah Matsell from Unbounce has some helpful tips on creating multi-step forms within the same page/url. This means you won’t need to paste in the second form url as the destination of your first form.

Note that this workaround allows you to create a form with one field per step, so this may not work for those who would like to have several form fields appear in a given step, however you can test out what works for you.

To create these multi-step forms on the same page:


Step 1.
Create your form in Unbounce.

Step 2.
Create a new button element for your ‘Next’ button and one for your ‘Previous’ button. Keep in mind when positioning these buttons (and your form submission button) that only one field will be shown at a time.

Step 3.
Copy the JS from ‘multistep_form.js’ and paste it into the Javascripts section of your page with placement ‘Before Body End Tag’.

Step 4.
Update the script with the ID of your ‘Previous’ and ‘Next’ button elements. Tip: Make sure you exclude the ‘#’ in the ID.

Step 5.
Copy the CSS from ‘multistep_form.css’ and paste it into the Stylesheets section of your page.

That’s it! See the whole process and the required code here.

Test out the technique on your next landing page

It might take a bit of practice to figure out the correct questions to be asking on your first step, or to find out the type of language to use on your form; but that’s what conversion rate optimization is all about: testing and trying new things to see what sticks. Ask the questions your visitors want answers to, and ask the questions your sales people need answers to to give a prospect a more personal answer.

If you give this a try, we would love to hear about your experience with a comment below.

Remember, all your forms (multi-step or otherwise) need to be GDPR compliant by May 25, 2018. See how to make your landing pages compliant by design and allow a visitor to opt-in here.

Read original article: 

Could the Breadcrumb Technique Help Boost Your Landing Page Conversions?