Digital storytelling in the age of blocks

This week, we released our first digital story; Diversity, inequality, and prejudice; a sociological exploration. This is the first in what will, hopefully, become a small series of stories that really define our mission, company culture, and broader vision. With these stories, we want to explore the possibilities the WordPress block editor has to offer for storytelling and creating rich interactive experiences.

You see, this kind of storytelling used to be really hard to do, and thus very expensive. There are some famous examples from renowned publishers like The Guardian's NSA files and the New York Times' Snowfall, but these could be classified as experiments or prestige projects. The rest of the world couldn't afford the technology to regularly create this kind of rich content. With the advent of the block editor in WordPress and some help from the Advanced Custom Fields plugin, this has now changed.

In this post, I will take you through the customization we needed to do in order to end up with a framework for digital storytelling. Let's take a look!

The basics: just use the block editor

Surprisingly, the block editor (using the latest version of the Gutenberg plugin) is enough to create most of the sections in our story. Anything requiring a grid layout with a background color or even a background image is now easy to do in the block editor. Let's look at the following example.

Have you ever tried creating this kind of section with the classic editor?

Now, let's look at how that is edited in the block editor.

You see, the WordPress block editor lets us effortlessly create, understand and navigate this complex structure. It shows us exactly how the blocks are nested and it has made it really easy to edit this section on any desired level. The editor screenshot might not yet 100% resemble the example section, but all it takes now on the frontend is a little CSS and we're done. Any WordPress developer could easily theme that!

Unfortunately, this version of the block editor is not in WordPress yet, but it will be included in WordPress 5.3, which is due to be released in November. If you want to start using this functionality today, you should install the Gutenberg plugin, version 6.4 or higher.

Effortlessly build custom blocks with ACF

As you can see, most layout challenges can now quite easily be solved with the block editor. That layout can also be filled with rich content like video's and images. But what if you need to do something more interactive? Let's look at another example:

What you see above is a simple card game. The player gets a statement and is asked to make a binary judgment about it. In this case it is "Stereotype" or "Prejudice", but you can also imagine the options would be "True" and "False".

A digital card game like this would always require a custom solution. Fortunately, this kind of solution is a breeze to integrate with the block editor using Advanced Custom Fields blocks capabilities. All we had to do to achieve this is create a field group, register that field group as a block and create a block template for it. ACF then gives us a block with custom fields we can now use in our digital story. Learn how to create your own ACF blocks here.

Configuring this game is now a no-brainer.

Reusing the Yoast FAQ block

Another example of an interactive block is the Trivia game we've added.

The trivia block is used to present the reader with a question to which they can imagine the answer. When the reader is ready, they can click the "show answer" link to check what the correct answer is.

We first started out creating an ACF block for this. But then we figured that the block actually has the exact same characteristics as the Yoast FAQ block. It has one or more questions, with answers which optionally contain an image. The only thing that differed was the output on the frontend. It would be great if we could reuse the Yoast FAQ block for this, and we would get the FAQ schema for free!

Fortunately that proved to be quite simple. The function register_block_type allows you to make any block dynamic and register a render callback for it. That way you can override the template. Let me show you the code:

register_block_type( 'yoast/faq-block', [
		  'render_callback' => [ $this, 'filter_storytelling_faq' ],
		] );

/**
 * Filters Yoast FAQ blocks for storytelling pages.
 *
 * @param $attributes The block attributes
 * @param $template The block template
 * @return string The filtered block template
 */
public function filter_storytelling_faq( $attributes, $template ) {
	if ( is_page_template( 'page-template-storytelling.php' ) ) {
		$newTemplate = '<section class="storytelling__faq game"&gt;';
		$newTemplate .= '<div class="row row__game row--small"&gt;';
		$newTemplate .= '<ul&gt;';
		foreach( $attributes['questions'] as $question ) {
			$listItem = '<li class="card game"&gt;';
			if ( preg_match('/.*?.json/i', $question['jsonImageSrc'] ) ) {
				$listItem .= output_lottie_animation( $question['jsonImageSrc'], false );
			} else {
				$listItem .= '<div class="img"&gt;';
				$listItem .= '<img src="' . $question['jsonImageSrc'] . '"&gt;';
				$listItem .= '</div&gt;';
			}
			$listItem .= '<details&gt;';
			$listItem .= '<summary class="game__question"&gt;';
			$listItem .= '<h4&gt;' . $question['jsonQuestion'] .'</h4&gt;<a&gt;Show answer</a&gt;';
			$listItem .= '</summary&gt;';
			$listItem .= '<div class="answer"&gt;';
			$listItem .= '<p&gt;' . preg_replace('/<img[^&gt;]+\&gt;/i', '', $question['jsonAnswer']) . '</p&gt;';
			$listItem .= '</div&gt;';
			$listItem .= '</details&gt;';
			$listItem .= '</li&gt;';
			$newTemplate .= $listItem;
		}
		$newTemplate .= '</ul&gt;';
		$newTemplate .= '</div&gt;';
		$newTemplate .= '</section&gt;';
		return $newTemplate;
	}
	return $template;
}

As you can see, in the render callback we get the block attributes and the original template. In case the page template is our storytelling template, we decide to generate different output and return it. Otherwise we just return the regular output for the FAQ block. Simple enough!

Getting the SVG animations to work

Recently, our illustrator Erwin has been experimenting with animating some of his awesome illustrations in Adobe After Effects. We figured it would be great if we could use these animations in our digital stories. It turns out you can export these animations in a JSON format in Adobe After Effects which can be parsed and rendered by a JavaScript library called Lottie.js.

Lottie can load any element with a "Lottie" class and a data-animation-path that points to the JSON, like this:

<div class="lottie" data-animation-path="https://yoast.com/app/uploads/2019/09/05_Nurture_switch.json" data-anim-loop="true" data-anim-type="svg"&gt;

Whenever you run lottie.searchAnimations() on a page, it will then load all animations it finds on the page. We used regular image blocks to insert the animations into the page. We needed to do a few tweaks to get this to work.

1. Add JSON to the allowed MIME types.

In order for content editors to upload these animations to the media library, we needed to allow them to upload JSON files to WordPress. We've used the upload_mimes filter to make that work.

2. Make Image blocks dynamic

When an image block contains a JSON source, we need to alter the output to what Lottie.js expects. Here's the code we wrote for that:

<?php
register_block_type( 'core/image', [
  'render_callback' =&gt; [ $this, 'filter_image_block' ],
] );

/**
 * Filters image blocks output to Lottie animations when applicable for storytelling pages.
 *
 * @param $attributes The block attributes
 * @param $template The block template
 * @return string The filtered block template
 */
public function filter_image_block( $attributes, $template ) {
	if ( is_page_template( 'page-template-storytelling.php' ) ) {
		$attachment_url = wp_get_attachment_url( $attributes['id'] );
		if ( preg_match('/.*?.json/i', $attachment_url ) ) {
			return output_lottie_animation( $attachment_url, false );
		} else {
			return $template;
		}
	}
	return $template;
}

/**
 * Outputs a div where Lottie can render an SVG animation.
 * Note: Make sure to call lottie.searchAnimations() in the JS of the page where this is rendered.
 * @link http://airbnb.io/lottie/#/web?id=other-loading-options
 *
 * @param string $network
 *
 * @return void | string
 */
function output_lottie_animation( $url, $echo = true ) {
	$output = '<div class="img"&gt;';
	$output .= '<div class="lottie" data-animation-path="';
	$output .= $url;
	$output .= '" data-anim-loop="true" data-anim-type="svg"&gt;';
	$output .= '</div&gt;';
	$output .= '</div&gt;';
	if ( $echo === true ) {
		echo $output;
	} else {
		return $output;
	}
}

3. Show the animations in the block editor

Ideally, an author can see the animations they add to a post in the editor itself. That, after all, is the idea of a WYSIWYG editor. We could hack the animation in with some very simple JavaScript:

(
	function( ) {
		wp.hooks.addFilter( "blocks.registerBlockType", "yoastcomBlockFilter", function( settings, name ) {
			if ( name === 'core/image' ) {
				var originalEdit = settings.edit;
				settings.edit = function( props ) {
					if ( props.attributes.url &amp;&amp; props.attributes.url.endsWith(".json") ) {
						window.setTimeout( lottie.searchAnimations, 500 );
						return wp.element.createElement( "div", { className: "img" },
							[
								wp.element.createElement( "div", {
									className: "lottie",
									"data-animation-path": props.attributes.url,
									"data-anim-loop": true,
									"data-anim-type": "svg"
								} ),
							]
						);
					} else {
						return wp.element.createElement( originalEdit, props );
					}
				};
			}
			return settings;
		}, 100 )
	}
)();

What we do here, is filter the edit function of the core/image block. When we detect a JSON file is uploaded to the block, we render the animation in the block editor. It's not perfect yet, since the block can no longer be edited once a JSON is uploaded to it. But it can still be removed and re-added. I admit this is somewhat of a hack in its current form and should be improved. But the result is quite magical. We've got animations rendering straight within the block editor, providing the author with a pretty accurate picture of what the page will look like on the frontend.

Conclusion

We built a digital storytelling framework, just by using the block editor, Advanced Custom Fields and Yoast SEO. We added a few tweaks here and there, but mostly this was a matter of writing CSS. It's quite impressive what we could achieve here with relatively little effort and it seems the block editor is getting even better with every release of the Gutenberg plugin. The future of rich interactive content is looking bright!


4 Responses to Digital storytelling in the age of blocks

  1. Scott Bourquin
    Scott Bourquin  • 2 months ago

    Very cool stuff! Thank you for showing us how to step up the game and create a more engaging experience.

  2. Mathukutty P V
    Mathukutty P V  • 2 months ago

    I have created home page without using page builder. It is fully created with Gutenberg and UAG blocks.

  3. TROY HACKING
    TROY HACKING  • 2 months ago

    Your diversity page is very impressive. However when I plugged it into Google PageSpeed insights to measure the loading speed it only scored a 10 out of 100 for mobile and 39 for desktop. That is a terrible score. Can you please address that as I love the features you've highlighted. Thank you!

    • Omar Reiss

      Hi Troy, you make a fair point! In a next iteration, I would definitely build in lazy loading of images, SVG animations, and video's. That's actually super doable with the same technique I used to filter the image block. I'd simply alter the media blocks to not render immediately and have them only render when they scroll into viewport. Since these kinds of digital stories are bound to be media heavy, they will still take up resources. But there is no reason why the initial page load and first meaningful paint couldn't be fast. Definitely still lots of improvement there!