Creating Blocks

When creating a new block, there is likely several places that we will need to prepare items in.

These could include:

  • Fields (Craft CMS)
  • Template Code (Twig)
  • Styling (SCSS)
  • Front-end Functionality (JS)

Configuring the Block Fields#

Your block will require Fields to be configured in order to function.

It is recommended to use a single field, with a field type that will allow you to nest fields under it - akin to a field group concept.

For this, we recommend one of the following field types:

  • A SuperTable field
  • A Matrix Field

You should set the field handle to be named the same as your component template:

For instance, is your block template is myAwesomeBlock.twig the you should use myAwesomeBlock as the field handle.

Adding the Template Code#

We need to create the template code that will be rendered for our block.

For this the process is not unusual:

  • Create a twig file in _inc/components/blocks/myAwesomeBlock.twig (our convention here is to use camel case for the file name)
  • Add your template code to your newly created twig file.
  • It is recommended that you add a unique class to your blocks first element, so that we can use it to target any required CSS and JS later.\ \ For our example block (myAwesomeBlock), our parent element could look similar to the following:
{# _inc/components/blocks/myAwesomeBlock.twig #}
<div class="block my-awesome-block">
... remaining block template code here ...
</div>
tip

When using the Block Builder or the Auto Block Renderer, fields will be passed into your Block in an object named data , so access fields within your template by referencing the data object: i.e. data.image or data.title

Styling Blocks#

  • Create an SCSS file for our new block in the relevant folder:

    src/scss/app/components/blocks/_my-awesome-block.scss

  • Import the new scss file in our block styles index file:

    /* src/scss/app/components/blocks/__blocks.scss */
    // Base Block
    @import 'block';
    // Heros
    @import 'hero-image';
    @import 'hero-video';
    @import 'hero-slider';
    // Sliders
    @import 'card-sliders';
    // Awesome Blocks
    @import 'my-awesome-block';
  • Add our styles to the new scss file, we should use the class name we defined when adding the template code to scope of styles to this block type only:

    /* src/scss/app/components/blocks/_my-awesome-block.scss */
    .my-awesome-block {
    backgroundColour: '#222222',
    color: '#eeeeee',
    width: '50%'
    ... other block specific styles here ...
    }

Adding Javascript Functionality to a Block#

This section covers adding JS functionality in the context of a block.

For documentation on general JS usage in Theme Base, refer to the JS Documentation.

The process for adding JS functionality to a block consists of a couple of major steps:

  • Create the JS File for our Block
  • Initialise the JS for all/any instances of our Block

Creating the JS File for our Block#

We can begin by creating a JS file for our block in the relevant folder if it exists, if not it can be created: src/js/app/components/blocks/myAwesomeBlock.js

A convention in theme base is to make use of ES6 Classes, since we have module loading available. So inside this file, we should first place the following skeleton class, and make sure it is exported, so that we can import it elsewhere.

// src/js/app/components/blocks/myAwesomeBlock.js
/**
* MyAwesomeBlock
*
* @class
*/
class MyAwesomeBlock {
/**
* @constructor
*/
constructor(blockElement = null) {
// ... add any additional constructor code here ...
if(blockElement !== null) {
this.blockEl = blockElement
this.init();
}
}
/**
* Initialise Block
*/
init(){
// ... do things with `this.blockEl` here ...
}
}
export default MyAwesomeBlock;

In the code above, we can see that the constructor receives a blockElement parameter, from looking at this we can gather that a single instance of our class is intended to operate on a single block element only.

If we wish, we can now add any additional JS code relating to our block, and then continue on to creating an instance of our class.

Initialising the JS for all/any instances of our Block#

In the previous step, we defined and exported a JS class that had code we would like to execute in relation to our block.

Before this code can be run, we must first create an instance of our class for every block element on our page.

We should do this by first importing our class into a Page Controller.

Each individual page/page-template in Theme Base is able to have it's own page controller, the contents of which is executed automatically when the page loads.

However, we also have the CommonPageController which is a controller whose code is executed upon loading any page in Theme Base. This is an ideal location for initialising blocks, as they can appear on any page, inside and outside of block builders and such.

We should import our class into the CommonPageController and cycle over all parent elements for our blocks, creating a new instance of our MyAwesomeBlock class for each one:

// src/js/app/pages/_common.js
import TailwindConfig from '../../../../tailwind.config.js';
import MyAwesomeBlock from '../components/blocks/myAwesomeBlock.js';
class CommonPageController {
constructor() {
console.info('CommonPageController::Constructor()');
this.init();
}
init() {
this.initHeroSliders();
// Let's add our call to initialse our MyAwesomeBlock's
this.initMyAwesomeBlocks();
}
initHeroSliders() {
for(let key in TailwindConfig.js.heroSliders){
let heroSliderEls = document.querySelectorAll('.hero-slider.' + key);
for (let index = 0; index < heroSliderEls.length; index++) {
const heroSliderEl = heroSliderEls[index];
// Start new card slider with config taken from the tailwind config
new SlickSlider(heroSliderEl, TailwindConfig.js.heroSliders[key]);
}
}
}
// Let's initialise our MyAwesomeBlocks
initMyAwesomeBlocks() {
// Select all of our matching blocks
let myAwesomeBlockEls = document.querySelectorAll('.my-awesome-block');
// Loop over each block
myAwesomeBlockEls.forEach((blockEl) => {
// Create an instance of this block
new MyAwesomeBlock(heroSliderEl);
});
}
}
}
export default CommonPageController;