Introduction to Blocking on the front-end

Phil Whittaker
Hi-Fi Agency
Published in
7 min readMar 12, 2017

We define a block as a part of UI functionality to include all the elements required to be viewed as a self-contained example. It includes 4 distinct parts; markup, style, data and schema. Blocks can be used within blocks and blocks are composed to create a page that can also be viewed as an example.

The purpose of blocking is to create a front-end solution that can display granular examples of the user interface separated from the production solution. The effort to move these files into production needs to be as minimal as possible, this simplifies the integration process and makes these blocks live previews of how the site will be displayed in different context.

We use Handlebars for templating, Scss for style, json to store data and json schema to define the structure of the data. It doesn’t matter what language or framework is used to write each of these blocks as long as there is no bleed of context between blocks.

Before you go through this example it might be worthwhile understanding the basics of Handlebars, it should be fairly simple but will make things easier to understand.

The easiest way to see how it works is to download our demo from github. Once it’s downloaded go to the frontend.src directory and run

npm install
gulp ui

A web browser should load up with the front-end development environment, if you go to the example page it will show a page containing 3 instances of the same block with different content in each.

Open the code in an editor and view the example page _dev/_templates/src/pages/examplePage, you will see 3 files and a data directory

  • _examplePage.scss : the scss file for this page
  • examplePage.hbs : the handlebars file for this page
  • examplePage.schema : the json schema file that defines the valid data for this block

The data directory has a single file that contains the data that is used to generate the example we saw in the browser. One example html file is rendered for each data file.

This page renders the block multiple times, filled from an array in the data file called gridItems. But if you look at the json file in the data directory you will see that it doesn’t contain any data, just a references to the json data files for the block.

"gridItems": [
{
"$ref" : "/parExampleBlock"
},
{
"$ref" : "/parExampleBlock_2"
},
{
"$ref" : "/parExampleBlock_3"
}
]

The data is stored against the block in its data directory, if you go to the block directory _dev/_templates/src/blocks/exampleBlock you will see 3 files in the data directory, these are the files that are referenced in the example page json file.

If you view this block in the browser examples then you will see that parExample has a + next to it. This means there are multiple states for this page. The 3 versions we saw in the data directory of the example block.

You can easily create a new example state for the page as well. In your code editor duplicate the examplePage.json to examplePage_new.json and move around the product array so that they are in a different order. Refresh the browser on the index page and you will see the new example state appear.

This is how we handle state examples in the front-end project, it allows us to see how a page or block will look when different data is used. This data is only used in the front-end solution, it isn’t sent to the production environment. It’s up to the front-end developer to use this powerful functionality to understand how the block is going to behave and more importantly create an example that is permanent so another team member can see how it should behave as living documentation.

Layouts

These are defined in the _dev/_templates/layouts directory and as minimum two are defined; each for the pages and block examples. We have a separate block layout to allow us to leave out the header or footer from block examples and display them in complete isolation. Each layout file comes with its own json data file to define what is rendered. Unfortunately currently json file references are not enabled in layout data json files so this file needs to contain all data including any blocks used.

You can add your own layouts by adding a new hbs and json data file. To signify the position in the code where the page content is inserted use

{{{ contents }}}

It is important to use triple curly brackets as the layout file renders to page as an html string here and the handlebars engine would encode the html.

To use this new layout in any page append the json data file with the layout name as below.

examplePage_2.layout2.json

This would create a new example state called 2.layout2 that used the layout2 file. In this way we can use different states to display how the page behaves using different layouts.

Displaying the full data on a page

Because the page json is built using references to json files there is no way in the code editor to view the fully composed json file that is used to render the page. At the bottom of the layout page is a reference to a special handlebars partial that writes this full json at the bottom of the page.

{{> parRawJson}}

Schema

These files define the format of the json data file that is to be used for a block or a page. Although not strictly required they do give a level of control and permanent definition to what a json data file could contain. Especially when each of the data files could be missing properties or have different configurations. It uses the same file referencing as the json data files so the schema blocks works as modules as well. The format is default json schema.

Style

The idea of a style block is to minimise bleed from between blocks, this makes the solution as maintainable and scalable as possible as it makes each block into an independent module. Of course we use global styles to define the scope of these blocks but everything that is specific to the block belongs here. We use BEM to define the class name structure.

Helpers

We use a few Handlebars helpers to help make the solutions easier to build. As we are rendering these hbs files on the production solution as well, it is important that both versions of the Helper produce exactly the same output. Below are the default helpers that we use on each project.

Modifiers

This helper injects a class name into the block from its parent. In this way we can modify the behaviour of the block from it’s context. Without this helper the wrapper tag defining the block would have to be defined in the calling page, which is messy.

{{{ modPartial ‘parExampleBlock’ this ‘block-left’ }}}

The modPartial helper takes 3 parameters; the block name, the data property and the modifier class to inject. The class is added to the data object in scope of the block modified, so you can use the below

<div class=”block-example {{modifier}}”></div>

It will inject the modifier class ‘block-left’ for the property modifier.

To use modifiers with blocks, it is important to add the property modifier to the schema definition for the block, otherwise when you add a modifier the schema will error saying there is an unexpected property of modifier.

IfCond

This extends on the if helper in handlerbars to include operators

{{#ifCond type ‘===’ “example”}}  {{> parExampleBlock this}}{{/ifCond}}

This is taken from this stackoverflow page

http://stackoverflow.com/questions/8853396/logical-operator-in-a-handlebars-js-if-conditional

Block definition

The hardest part of blocking it to define what a block is in your user interface. Too large and blocks quickly become unwieldy and complex, too small and you get too many blocks making the solution hard to maintain. After a lot of trial and error we have come to the conclusion that it’s easier to refactor a larger block than to recombine existing blocks into one. So we advise creating larger blocks and then refactoring overtime when necessary.

Moving to production

When you are ready to move to move the front-end solution into production then you can run.

gulp dist

This combines and minimises all the styles and copies all the markup files and schema into one directory. It copies this over to the production server which can then use them in the application.

In our continuous delivery process we run a dist on the server build to allow front-end developers to update the staging site without first completing a dist locally. As long as the format of the json data files hasn’t changed then the new update will change the UI to the site without any intervention.

This may look like a very simple solution but it scales very well. It gives the front-end dev the space and freedom to generate a style guide for the all the components and pages of the site. Because we have defined the dynamic data on each page, integration becomes really simple as well. No more messy, big balls of mud and string, just clean and maintainable user interfaces.

--

--