Many of the articles I’ve shared recently describe specific features in the Gutenberg block editor and the Genesis theme framework.
I think it would be helpful to take a step back and walk you through my process of building a custom WordPress theme from start to finish. I encourage you to also review my Developer’s Guide to Gutenberg.
I focus on a recent Genesis child theme I built for Her Packing List, but the process applies to any WordPress theme.
Table of Contents
Start with design
Gutenberg has accelerated a change in the way we design and build themes. We still use a three phase process of Discovery, Design and Development, but we now think in more modular terms.
Rather than designing in pages, with each page often being a unique template with its own styling and metaboxes, we design in blocks and assemble the pages using these blocks.
We actually used a modular design approach before Gutenberg, but were constrained on the development side by the classic editor. Our designs used reusable components, but these were often managed with metaboxes and shortcodes. Now with Gutenberg our modular design can be implemented with core and custom blocks.
It’s crucial to work with a designer who understands the Gutenberg block editor. My two design partners, Duane Smith and Andrew Pautler, create incredible web experiences both through their design expertise and by enabling our clients to fully use the features of WordPress.
Thank you! I am just blown away at how well everything is thought through in your development! I LOVE it!
Robyn Stone
Add a Pinch
I won’t go into any more detail on our design process, but I think it’s important to note that a key factor in building an easy-to-manage website is starting with a design that was built for the block editor, rather than trying to squeeze a custom design into the block editor.
Style Guide
After I’ve set up my development environment and download a fresh copy of my starter theme, I start development by building out the Style Guide.
The above is just a sampling from the style guide. Here’s the full style guide: desktop | tablet | mobile.
First I update my _base.scss SASS file with brand colors, Gutenberg color options, grid width, and breakpoints. Here’s the _base.scss file for Her Packing List.
I review the core blocks to see which will need additional block styles. This site had quite a few – underline and fancy styles for headings, arrow and icon styles for lists, and more. Here’s the editor.js file for this project.
In the content editor I build the Style Guide page with all the content from the mockup, up until the “Custom Gutenberg Blocks” section. Then I write the CSS in _blocks.scss so the Style Guide matches the mockup.
Custom Blocks
I build all of my custom blocks using ACF. I avoid using block library plugins like Atomic Blocks and CoBlocks because they contain many blocks that we don’t need, and the ones we would use often allow customizing the styling (ex: specify padding for each block).
I don’t want content creators to worry about design. They should focus on content creation and let the theme handle the styling. ACF makes it incredibly easy to quickly build custom blocks with minimal options, keeping the interface as simple as possible for content creators.
Before I start coding, I review the visual and technical requirements for each custom block and determine how I’ll build it.
The above is just a sampling from the style guide. Here’s the full style guide: desktop | tablet | mobile.
- Social Media Share Buttons will be implemented with Shared Counts. I’m not actually building a custom block for this since they will automatically be added to the end of every post by Shared Counts. I just have to style them to match the design.
- Social Media Share Links will be an ACF block with no options. It will pull social links from Yoast SEO.
- Pinterest CTA will be an ACF block with two image upload fields.
- Email Subscribe CTA will be a WPForms block with some CSS.
- HPL World CTA will be an ACF block with no options.
- Email Contact Form is WPForms.
- Product Listing is an ACF block with a title and repeatable field for products. Each product has a name, image, URL, and price.
- Quick Links is an ACF block with a repeatable field for links.
- Gear We Use and Travel Resources use the WP core group block with a background color set. They are saved as a reusable blocks since they are used on multiple pages.
- Related Posts is an ACF block with no options. It will be used in the Single Post Block Area.
- Next Stop is an ACF block with no options. The dropdowns are populated with terms from a custom taxonomy and take you to the term archive.
- Fact Box is the WP core group block with a background color.
See my article Building a block with Advanced Custom Fields for information on building custom blocks. Also take a look at the sample code at the end of ACF with version control to see how I organize the ACF code in the theme.
Gutenberg Specific Styling
After ensuring all of the blocks match the design on the frontend, it’s a good idea to switch back to the Gutenberg block editor and make sure your blocks look the same there too.
The backend editor should match the frontend as close as possible so content editors have a good idea of how the page will look before publishing.
If you’re using SASS, it’s easy to generate an additional stylesheet with only the relevant block styles, then enqueue it as editor styles. My editor-style.scss loads:
- _base.scss with my variables for colors
- _blocks.scss with my block-specific styling
- _gutenberg.scss for Gutenberg-only styling
WordPress loads some CSS only in Gutenberg that can alter the styling of your blocks. I use my Gutenberg-only partial for minor tweaks to get the backend blocks matching the frontend.
I also updated the post title to be white on a red background, and customized the sizes of wide and full blocks. Here’s my _gutenberg.scss for Her Packing List.
Finish building the base template
Now that the blocks in the content area are complete, I focus on everything that’s not in the block editor. This includes building out the site header, navigation menu, sidebar, and site footer.
Once the Style Guide is complete, I move over to the Single Post design and build out the additional post elements, like the comments and author box.
All of the elements after the author box are managed with the After Post Block Area, described in its own section below.
Genesis can now automatically add the featured image to the top of posts, and let writers disable it on a per-post basis. Add the following to your theme’s functions.php file:
add_post_type_support( 'post', 'genesis-singular-images' );
See the “Featured image output options” section of the Genesis 3.1 release notes for more information.
Archives
After building out all the singular content (Style Guide, Single Post, and individual pages), I move on to the dynamic archives.
My approach to archives is a bit different than most Genesis theme developers, but will be familiar to those building custom themes outside of Genesis.
What makes Genesis unique are its hooks and filters. These allow you to customize any aspect in Genesis, and insert your own elements wherever you’d like. Want to add something before the post title? Use the
hook with a priority less than 10.genesis_entry_header
These same hooks run when a post is on an archive page, so any customizations you make need to be scoped with conditional tags (ex: is_single()
). If you’re using genesis_custom_loop()
for related posts, you need to limit your customizations to the primary post on the page using if( get_the_ID() === get_queried_object_id() )
.
Scoping your customizations is even more difficult if you use a modular design approach like we do. We’ll typically have a few different “post summary” styles used throughout the site, on the archive page, in a related posts section on a single post, and in custom “Post Listing” blocks on landing pages.
I prefer to remove the standard genesis loop (and all of its hooks) on archive pages and use template partials instead. I can then design the “post summary” styles as template partials and use them wherever they are needed.
On Her Packing List I built 3 different archive partials, found in /partials/ directory of the theme.
archive.php
echo '<article class="post-summary">';
echo '<a class="entry-image-link" href="' . get_permalink() . '" tabindex="-1" aria-hidden="true">' . get_the_post_thumbnail( get_the_ID(), 'ea_archive' ) . '</a>';
echo '<p class="post-summary__meta">' . ea_icon( array( 'icon' => 'chat-circles', 'size' => 16 ) ) . get_comments_number_text() . '</p>';
echo '<h5 class="entry-title"><a href="' . get_permalink() . '">' . get_the_title() . '</a></h5>';
echo '</article>';
archive-related.php
echo '<article class="post-summary related">';
echo '<a class="entry-image-link" href="' . get_permalink() . '" tabindex="-1" aria-hidden="true">' . get_the_post_thumbnail( get_the_ID(), 'ea_archive', array( 'class' => 'nopin' ) ) . '</a>';
echo '<h3><a href="' . get_permalink() . '">' . get_the_title() . '</a></h3>';
echo '</article>';
archive-featured.php
echo '<article class="post-summary featured">';
echo '<a class="entry-image-link" href="' . get_permalink() . '" tabindex="-1" aria-hidden="true">' . get_the_post_thumbnail( get_the_ID(), 'large' ) . '</a>';
echo '<div class="post-summary__content">';
echo '<h2 class="is-style-underline">Featured Post</h2>';
echo '<h2 class="entry-title"><a href="' . get_permalink() . '">' . get_the_title() . '</a></h2>';
the_excerpt();
echo '<p class="post-summary__meta">' . ea_icon( array( 'icon' => 'chat-circles', 'size' => 16 ) ) . get_comments_number_text() . '</p>';
echo '</div>';
echo '</article>';
In my archive.php template file, I made the first post on the page use the archive-featured.php partial with:
/**
* Featured post partial
*
*/
function ea_archive_featured_post( $context ) {
global $wp_query;
if( ! get_query_var( 'paged' ) && 0 === $wp_query->current_post )
$context = 'featured';
return $context;
}
add_filter( 'ea_loop_partial_context', 'ea_archive_featured_post' );
For more information on this approach, see Using Template Parts with Genesis.
Block Areas
Our sites often have repeated elements outside the content area, like related posts at the bottom of a single post.
You could hardcode these features into the template file, or create a widget area so your client can edit it.
We prefer creating Block Areas, which are like widget areas but use the block editor.
To include the “After Post” block area to our single posts, I added this to single.php:
/**
* After Post modules
*
*/
function ea_after_post_area() {
if( function_exists( 'ea_block_area' ) )
ea_block_area()->show( 'after-post' );
}
add_action( 'genesis_after_entry', 'ea_after_post_area', 9 );
Block Area in Archives
We also provided the ability to insert block areas inside archive pages.
On certain categories, our client wanted to use the “Post Listing” block we created to list recent posts in subcategories of the current category. These would appear after the first (“featured”) post, and before the second post.
I edited the block_area
post type registration to include categories and tags as supported taxonomies. Then on archive pages, I do a taxonomy query to see if any block areas have the current category, and if so I output the block area after the first post.
/**
* Archive block area
*
*/
function ea_archive_block_area() {
if( get_query_var( 'paged' ) )
return;
global $wp_query;
if( 1 !== $wp_query->current_post )
return;
$tax = is_category() ? 'category' : ( is_tag() ? 'post_tag' : false );
if( ! $tax )
return;
$loop = new WP_Query( array(
'post_type' => 'block_area',
'posts_per_page' => 1,
'tax_query' => array(
array(
'taxonomy' => $tax,
'field' => 'term_id',
'terms' => array( get_queried_object_id() )
)
)
));
if( $loop->have_posts() ): while( $loop->have_posts() ): $loop->the_post();
echo '<div class="block-area archive-block-area">';
the_content();
echo '</div>';
endwhile; endif; wp_reset_postdata();
}
add_action( 'genesis_before_entry', 'ea_archive_block_area' );
Modular Template
Her Packing List doesn’t include a modular template, but I’m including a note here because many of the sites we build do have them.
I think of Gutenberg as a “rich content builder”, but not a page builder or layout builder. It’s not a good tool for things like displaying content across multiple columns on a grid and adjusting that layout at different breakpoints.
I hope Gutenberg never becomes a full-on page builder because that will drastically increase the complexity of content editing. I’m a firm believer in the separation of content and design.
Some pages are too difficult to implement directly in the Gutenberg block editor. On the sites we build, these are often homepages, landing pages, and category landing pages.
I create a modules.php
page template that disables the editor, then loads an ACF metabox with a Flexible Content field. We’ll design and build a dozen or so modules. For more information, see Landing Pages with ACF Flexible Content.
During our discovery and design process we identify the types of blocks/modules needed and their overall complexity. We try to use blocks for everything if possible, like we did with Her Packing List.
If the elements reach a certain level of complexity, we’ll work with our client to either scale back the complexity to something manageable with blocks, or start separating elements into blocks and modules.
The downside to modules is you lose the flexibility and visual editing of a block editor, but it can greatly simplify the content creation process on complex pages.
This is a conversation we have with the client during discovery and design. Our goal is to make their site as easy and enjoyable to manage as possible, so they should be involved in deciding how we build these features.
Sebastian says
Great article, thank you.
I only create custom wordpress themes for clients. Since years i use ACF Flexible Content Fields to give the clients a pool of blocks fits to there needs. So it is the same idea as Gutenberg Blocks.
Until now i didn’t use Gutenberg Blocks. Principle i love the idea that the editor create a post / page and have a feeling how it looks on the frontend.
But if i style every block exactly how it looks in frontend the cost of developement for the theme increase highly, especially for complex blocks like a slider or custom gallery or something like that.
Furthermore if i change styles for an element of the site i have do that twice. What’s about the responsive styles? Do you have the same breakpoints like in frontend?
If i work with ACF Flexible Content Field it looks a littlebit abstract for the editor in backend but it does its job.
So do you have any experience for a project calculation how much the effort increases if i use Gutenberg Blocks instead of ACF “Blocks”?
Many thanks in advanced for sharing your experience.
Bill Erickson says
It shouldn’t require any additional work to style the block for the backend editor. Since the exact same markup is used for the block on the frontend and backend, the CSS you wrote for the frontend should work in the block editor as well with no/minimal changes.
I find that switching from ACF flexible content to ACF blocks actually decreased our development time, while increasing the client’s capabilities.
We don’t have to re-invent the wheel by creating ACF flexible content areas for “Content”, “Quote”, “Image” etc, items that are already built into core. We don’t have to build our own global styling functionality, where each ACF flexible content field has options for background color, alignment, grid width, etc. We can use the group block in WP core for layout / background colors and insert our own custom blocks inside of it.
We can use a more atomic approach to custom blocks because you can insert blocks inside of other blocks. For instance, this block just has a single
field so the client can add any content they like to it. I don’t have to create a title field, WYSIWYG content field, add a Link field in case they want to add a button…As mentioned at the top of this blog post, the most important aspect (IMHO) is an atomic design approach from the start of the project. If you’re handed a design where every page has its own unique elements and minor design quirks, you’ll have trouble implementing that in the block editor. But if your design builds upon core blocks, so the buttons in your custom blocks look/work the same as buttons in other contexts, you can quickly build your custom elements and leverage core blocks for everything else.
Sebastian says
Thank you so much for your reply Bill.
Now i’m motivated to give it a try on the next project.
Thanks again for your time and that you gave me the push to try it out!
Lesia says
Thanks for the great article!
I also strongly dislike all those clumsy constructor plugins..
and more important is that I usually need a completely custom design, so I use gutenbers groups as a base blocks for my css. I also include that css on backend to make content look similarly to the frontend, this needs some further work though to look better 🙂
I wrap content in groups and sometimes assign classes to them (in case of more complex nesting or repetitive design).
The downside of this approach is that a client later may break the proper nesting, so sometimes I doubt if I’m doing all this right🤔 What for you think of this method?
Bill Erickson says
I use groups for basic alignment (ex: wide or full width sections) and background colors. Anything with a fairly complex layout is built as a custom block, especially if the columns / elements change at different breakpoints.
David Hedley says
Hi Bill, I’d love to get your thoughts on where we go from here in 2022 with the Genesis Framework, and whether you feel using Genesis Sample as a starting point for a new block-based production site is still a safe option given the launch of full site editing? I really do not want to lose the Genesis way of doing things, but also need to ensure a certain degree of future-proofing for client websites.
Bill Erickson says
The “Genesis” way of doing things is not compatible with the new FSE approach to themes. FSE themes are HTML files that have pre-configured blocks in them. There are no traditional PHP theme files, so everything that makes Genesis great (ie: the hooks & filters) are not applicable in a FSE world.
I put “Genesis” in quotes because this was also the “standard WordPress” way of doing things, and Genesis itself is pivoting to the “new WordPress” way by rethinking Genesis as a block plugin.
Personally it will be many years (if ever) when I start building FSE themes. My clients don’t want a full-site page builder where they can easily break or change the style of the site.
I think FSE is a big improvement for WordPress users who need a DIY approach and don’t know code. Instead of trying to find a theme that’s close to their needs and hiring a dev to customize it, they can customize it themselves right in WordPress. But I don’t think FSE will impact the higher level of the market where businesses want a professional, bespoke, branded theme that properly separates content from design so their content editors can focus on creating content and not designing pages.
I don’t think traditional (non-FSE) themes are going anywhere. Unlike the classic editor which has been deprecated for the new block editor, I think we’ll permanently have two theming methods as they serve two separate needs. Even though WordPress has recently neglected its commitment to backwards compatibility (every major WP release seems to break / change something related to Gutenberg), there’s no way WP could ever leave the traditional theming method behind. You can’t convert a traditional theme to a FSE theme, and 40% of the web is using a traditional WP theme right now.
I am a big fan of theme.json though, and should have a detailed blog post up soon showing how to use it and why it’s great.
I don’t use Genesis much anymore, but that’s not a criticism of the Genesis framework. My starter theme uses the Theme Hook Alliance which is like the Genesis hooks but used by non-Genesis themes. Ex: instead of the
genesis_loop
hook I havetha_content_loop
. I’m still building things “the Genesis way” with a hook-based template, but now it’s a parent theme so we can create site-specific child themes, which is a common need for my clients.