Building a Gutenberg website

Over the past few months we’ve built a few websites using WordPress’ new block-based editor, code-named Gutenberg. I’d like to share how we approach these projects and provide some real-world examples. I’ll then get share some technical details you’ll need as a WordPress developer building a Gutenberg website.

There are really two types of Gutenberg websites: simple and custom.

Simple Gutenberg Websites

Gutenberg is a huge step forward for simple content sites. Clients can easily build beautiful and engaging pages of content with the core blocks. The theme development process for these sites is mostly CSS. We ensure the core blocks look great, match the client’s brand, and can address all their content requirements.

This is perfect for non-profits who need a low-cost, high-impact website. We used this approach for CARAS and College Discount Cards.

We start by designing a style guide featuring all the core blocks. Our designer then works with the client to mock up the key pages using these blocks. Once the designs are approved, I style the blocks and build the pages like legos.

Custom Gutenberg Websites

These sites follow our traditional web development process. Rather than letting the technology constrain our design and functionality, we first examine the client’s specific needs and create discovery documentation outlining the user experience. We then design the user interface, mockup all the blocks (both core and custom), and mockup the page layouts necessary to build the site.

In addition to styling the core blocks, we build custom blocks to implement features not currently in core. The above example of Free Together includes a “Table of Contents” block that dynamically lists and links to each heading in the article.

On RCP (soon to launch) we built many custom blocks including a Feature Box, Icon Links, and Downloads.

Is Gutenberg the right choice?

Gutenberg is still beta software. There are many small bugs, and plugin updates can bring breaking changes. I’ve had to update a site three times in the past two months because Gutenberg updates to the Columns block broke content already built with columns.

You have to consider the value proposition for each project. We currently lean towards using the classic editor unless there’s a compelling reason to use Gutenberg. Those compelling reasons often include:

  • Building a simple Gutenberg site will save us from building a more complex website that relies heavily on custom metaboxes. We can deliver a better product faster and cheaper.
  • The website requires complex content elements that are easy to implement as custom blocks but difficult to do with TinyMCE. For instance, the RCP project required 9 custom blocks. Before Gutenberg, these would be a mix of shortcodes with Shortcode UI, custom TinyMCE plugins, and metaboxes.
  • Our team will be entering most of the site content, so we’re comfortable working around Gutenberg bugs.
  • The site is unlikely to be redesigned for a long time, so we’d rather future-proof it by using the upcoming block editor.

Here’s the “Feature Box” block we built on RCP. This is a great example of how Gutenberg can make content creation easier. Something like this would be difficult and complicated to insert in TinyMCE.

Frontend / Backend Preview
Backend Edit

Gutenberg is not a page builder

Gutenberg is a block-based content editor, not a page builder. It’s hard to describe, but it will become more clear as you use it in the real world.

While the columns block does allow you to nest blocks inside of blocks, it’s fairly primitive and difficult to set up. The editing experience can be painful and you’ll need custom CSS to make it mobile responsive. It works for simple sites, but is not a replacement for true page builders like Beaver Builder or flexible content areas.

We use Gutenberg for most content pages on the site; it replaces a lot of custom page templates we would have developed previously. But for certain pages like the homepage and landing pages, we’ll often disable Gutenberg for those templates and build a true page builder with Carbon Fields or Advanced Custom Fields.

Building a Gutenberg theme

For the most part, a Gutenberg-optimized theme is just like any other WordPress theme. Your current WordPress theme will likely work great with Gutenberg.

Add Theme Support

You should consider adding theme support for the new Gutenberg features. See my base child theme as an example. I’ve placed this code in functions.php.

Wide/Full Alignment

add_theme_support( 'align-wide' );

This adds “wide” and “full” options to the left, right, and center alignment options. This will be used primarily for images, but other blocks also support these alignment options.

You’ll need to add CSS to your theme for these alignment options to actually work, in the same way you have to style .alignleft for left-aligned images to work. The actual CSS you use will based on whether you wrap or not.

Editor Font Sizes

add_theme_support( 'editor-font-sizes', array(
'name' => __( 'small', 'ea_genesis_child' ),
'shortName' => __( 'S', 'ea_genesis_child' ),
'size' => 12,
'slug' => 'small'
'name' => __( 'regular', 'ea_genesis_child' ),
'shortName' => __( 'M', 'ea_genesis_child' ),
'size' => 16,
'slug' => 'regular'
'name' => __( 'large', 'ea_genesis_child' ),
'shortName' => __( 'L', 'ea_genesis_child' ),
'size' => 20,
'slug' => 'large'
'name' => __( 'larger', 'ea_genesis_child' ),
'shortName' => __( 'XL', 'ea_genesis_child' ),
'size' => 24,
'slug' => 'larger'
) );
view raw functions.php hosted with ❤ by GitHub

When editing paragraph text, a user can select different sizes in the settings panel on the right. This lets you define what sizes should be available. I’ve commented out the extra large size since we only use small, medium, and large.

You’ll also have to add CSS to your theme for this to work. Rather than hardcoding the font size on the paragraphs, Gutenberg adds a CSS class like .has-small-font-size. This helps keep the content separate from the styling and will simplify redesigns in the future.

Theme Colors

// -- Disable Custom Colors
add_theme_support( 'disable-custom-colors' );
// -- Editor Color Palette
add_theme_support( 'editor-color-palette', array(
'name' => __( 'Blue', 'ea_genesis_child' ),
'slug' => 'blue',
'color' => '#59BACC',
'name' => __( 'Green', 'ea_genesis_child' ),
'slug' => 'green',
'color' => '#58AD69',
'name' => __( 'Orange', 'ea_genesis_child' ),
'slug' => 'orange',
'color' => '#FFBC49',
'name' => __( 'Red', 'ea_genesis_child' ),
'slug' => 'red',
'color' => '#E2574C',
) );
view raw functions.php hosted with ❤ by GitHub

Gutenberg includes a color picker in certain blocks like paragraph and button. Rather than letting clients pick any color they want, we disable the color picker and define a few brand colors.

Like all the other theme options, these require additional CSS to function properly. When a block has the “Background Color” set to one of your theme colors, it adds a class of .has-{color}-background-color. Likewise, when the “Text Color” is set, it adds a class of .has-{color}-color.


I can’t imagine building a Gutenberg theme without SASS. You need to generate a frontend stylesheet, classic editor stylesheet, and a Gutenberg stylesheet, all with differing amounts of CSS, and the Gutenberg parts need to be prefixed. Look at the scss directory of my base theme to see how I’m structuring it.

For the Theme Colors feature described above, we use SASS loops to build the styles.I define all our colors and other variables in _base.scss, then loop through them in my _style-guide.scss:

// Brand Colors
// -- normal / darker / lighter
$blue_1: #59BACC;
$blue_2: #39A5B9;
$blue_3: #80CAD8;
$green_1: #58AD69;
$green_2: #458D53;
$green_3: #7ABE88;
$orange_1: #FFBC49;
$orange_2: #FFA916;
$orange_3: #FFCF7C;
$red_1: #E2574C;
$red_2: #D83023;
$red_3: #E98078;
// Gutenberg color options
// -- see editor-color-palette in functions.php
$colors: ( blue, $blue_1 ),
( green, $green_1 ),
( orange, $orange_1 ),
( red, $red_1 );
view raw _base.scss hosted with ❤ by GitHub
/* Color Options
--------------------------------------------- */
@each $name, $color in $colors {
.has-#{$name}-color {
color: $color;
.has-#{$name}-background-color {
background-color: $color;
view raw _style-guide.scss hosted with ❤ by GitHub

Don’t Wrap

Every site we built pre-Gutenberg used <div class="wrap"> around the content area to limit the max-width of the content area. This works great when everything is the same width, but makes the wide / full alignment options difficult.

If you’re adding Gutenberg support to an existing theme, you likely don’t want to change the markup of the site. In that case, leave the wrap as-is and use negative margins to break full and wide blocks out of the wrap (more information).

On brand new themes, I’m using Twenty Nineteen as a guide and not wrapping the content area. Instead, we set a max-width on the individual blocks and let the alignment override that.

.entry-content > * {
margin: 32px $base-margin;
max-width: calc(100vw - (2 * #{ $base-margin }));
@include media(">=tablet") {
margin: 32px calc(2 * (100vw / 12));
max-width: calc(8 * (100vw / 12));
@include media(">=desktop") {
max-width: $grid-max-width;
margin-left: auto;
margin-right: auto;
> *:first-child {
margin-top: 0;
> *:last-child {
margin-bottom: 0;
&.alignwide {
margin-left: auto;
margin-right: auto;
@include media(">=tablet") {
margin-left: calc(1 * (100vw / 12));
margin-right: calc(1 * (100vw / 12));
max-width: calc(10 * (100vw / 12));
&.alignfull {
margin-top: calc(2 * #{$base-margin});
margin-right: 0;
margin-bottom: calc(2 * #{$base-margin});
margin-left: 0;
max-width: 100%;
&.alignleft {
float: left;
max-width: calc(5 * (100vw / 12));
margin-top: 0;
@include media(">=tablet") {
max-width: calc(4 * (100vw / 12));
margin-right: calc(2 * #{$base-margin});
@include media(">=desktop") {
max-width: calc(3 * (100vw / 12));
&.alignright {
float: right;
max-width: calc(5 * (100vw / 12));
margin-top: 0;
margin-left: $base-margin;
margin-right: $base-margin;
@include media(">=tablet") {
max-width: calc(4 * (100vw / 12));
margin-left: calc(2 * #{$base-margin});
margin-right: calc(2 * (100vw / 12));
view raw _blocks.scss hosted with ❤ by GitHub

Set block width in Gutenberg

The above styles only apply to the frontend. Gutenberg already has some base styling applied to blocks, but their max-width doesn’t match the max-width we are using on the site. We use the following in gutenberg.scss to more closely mirror the frontend:

body.gutenberg-editor-page.wp-admin {
/* Main column width */
.editor-block-list__block {
max-width: $grid-max-width;
/* Width of "wide" blocks */
.editor-block-list__block[data-align="wide"] {
margin-left: calc(1 * (100vw / 12));
margin-right: calc(1 * (100vw / 12));
max-width: calc(10 * (100vw / 12));
/* Width of "full-wide" blocks */
.editor-block-list__block[data-align="full"] {
max-width: none;
view raw gutenberg.scss hosted with ❤ by GitHub

Building Custom Blocks

The biggest question most theme developers will have is “Should I build my custom blocks from scratch in JavaScript, or use a plugin like Advanced Custom Fields?”

I think metaboxes are a great analogy for this. When I’m building a custom plugin that will be distributed and used by many, like Genesis Title Toggle, I build my metabox from scratch. This removes a plugin dependency (ACF) or a bundled library (CMB2) and keeps the plugin lean.

When I’m building a bunch of metaboxes for a specific client’s site, it’s better to leverage an existing tool like Advanced Custom Fields, Carbon Fields, or CMB2. I’m able to provide a higher quality result much faster than if I were to build everything from scratch.

I use the same approach to Gutenberg blocks. For client sites, I’m building my custom blocks with Advanced Custom Fields. When I release a public plugin that includes a block, it will be built from scratch.

My next post will walk you through building custom blocks with Advanced Custom Fields. If you’d like to be notified when that post is ready, fill out the “Receive New Posts by Email” box below.

Receive New Posts by Email


  1. Great post, Bill. Thanks for sharing. Just did my first theme with Gutenberg this week, looking forward to seeing this software evolve.

  2. Custom blocks with ACF looks great and seems to greatly simplify the process. The downside is having to install ACF again – as you’ve written previously, ACF is slow. I moved a number of sites to CMB2 so it’ll feel like a backwards move.

    1. I’ve been told ACF has fixed most of its performance issues, but this is my first time using it in years so I haven’t tested it for myself. I’d like to do some performance testing at some point to compare it to other solutions.

      Carbon Fields, my preferred metabox solution (see here), will have block support soon. I’m sure there will be more block building plugins over the next year or two as Gutenberg matures.

  3. Bill,

    I agree with a lot of your points about things to be aware about when building a site with Gutenberg. I’ve built two now, and have learned quiet a bit each time. I had a question and just wanted to get some feedback and idea share.

    When it comes to spacing out the content area, I came up with a unique way to make spacing for the content area to allow for centered, wide width, and full width without sacrificing the wrapper padding. For example if my content container has padding: 0 4em and I apply margin-left: -4em; margin-right -4em; to my full width class it does align as expected and works well on devices. It seems to work very with especially when combined with Sass.

    Just wanted to share this in case it’s helpful for someone else. What are your thoughts on this idea? I can share an example site with you if you like.

    1. Yes, that would definitely work. But it sounds like you are still not setting a max-width on the content area, just adding additional left/right padding.

      The “wrap” I refer to is usually: .wrap { max-width: 980px; } defining the maximum width of the content area.

      If you have a fixed max-width and a variable page width (ie: you don’t know how wide the user’s browser is), it makes negative margins difficult.

      1. That is a good point, I typically don’t set too many max widths so it does seem to work well my style, I’ll keep that in mind for future sites where max-widths may be used more frequently.

        Thanks for taking the time to reply. I look forward to reading more about your experiences on Gutenberg.

  4. Thank you so much for sharing your Gutenberg experiences and tips! I’m really nervous about how this will all play out; having your background and opinions is very helpful.

  5. Thanks so much for sharing and giving me a chance to see the code in action!

    nb. has got horizontal scrollbar on all browsers I checked.
    You did not use display:grid? For example the block with the 3 buttons would have been more simple with layout grid? Is the a specific reason you only used flex?

    1. That’s strange. I’ve tested in a bunch of browsers and am not seeing a horizontal scrollbar. Can you identify what element is sitting off screen and causing the scrollbar?

      The styling for the columns block comes from Gutenberg itself. Gutenberg originally built it using CSS Grid, but recently rebuilt that block to use flex. I’m not sure why they rebuilt it, but that change broke all the instances of columns on this site (especially the additional grid styling I added for mobile).

      We built College Discount Cards about 6 months ago. If I were to rebuild it today, I would create a custom block using ACF for that area so I could have complete control of the styling. And yes, I’d use CSS grid for that 🙂

      Sometimes getting the core blocks to behave the way you’d like is more work than building something custom.

      1. Scrollbar on all browsers in WIndows10 – not on Mac!
        When I remove ‘width: auto’ from .alignfull (_style-guide.scss:340) the scrollbars disappears. (did not test further)

        Your experience with the changes is why I as a one-man band keep an eye on Gutenberg, but I do not want to use it on live sites yet, just too much work/risk. That makes your posts very valuable to me… so thanks again 🙂
        I did spend time to learn about css grid, so your confirmation that you would do it different is confirmation that I learned those lessons well 🙂 (Sofar way more excited about grid then about Gutenberg)

  6. This is a very useful post for us devs preparing for the WP5 rollout. Thank you Bill.

    I’m curious to know what method you are using to process SASS in your setup. Are you using a plugin?

    1. I use CodeKit for SASS and JS processing. It works great when you’re a solo developer.

      If I’m working as part of a team (rare for site development, more common for our public plugins like Shared Counts) we use Gulp to ensure consistent processing across team members.

  7. Thank you for sharing – in particular I was interested to read your take on building custom blocks – ACF vs from scratch. It’s something I’m grappling with currently.

    ACF somehow feels like ‘cheating’ (I think because I read somewhere that using ACF is only a temporary ‘stop-gap’ solution – which makes me nervous of using it on projects where the site could be in existence for say 5+ years). But it’s perhaps the most pragmatic approach for smaller projects. How concerned do you think we need to be that using ACF means we’re moving back away from a WYSIWYG editing experience (one of the main goals of Gutenberg)??

    I’m also scratching my head about whether it’s ok to store structured data in standard block attributes (i.e. that aren’t set to save to post meta). Take the use case of a custom post type (e.g. project) for which we create a block template with a mixture of core/custom blocks. If you then wanted to pull out a grid of ‘most recent projects’, which included e.g. a ‘client name’ attribute, is that doable? I haven’t found a way yet (although guess in theory it’s possible to parse through the whole of the CPT content) – other than setting the attribute to save to post meta ( Reading ACF’s post (, it seems their scope is limited to post content, rather than meta.

    Sorry for the long-winded ‘thinking out loud’ comment! It’s very helpful to hear from other devs working on this…

    1. You bring up some great points.

      We’re entering the wild west of block creation right now, and I think it will take a year or two for best practices to become accepted.

      ACF is the first “block builder” plugin on the market, but it won’t be the last. I know Carbon Fields is adding block support. I expect to see more plugins that are purely block builders, rather than metabox plugins that are adding block support.

      I think these newer block builders will be more true to the goals of Gutenberg, providing a visual editor experience that closely mirrors the presentation, rather than ACF’s “data entry as a metabox” model.

      Gutenberg itself is still changing rapidly. I can’t justify building a custom block from scratch for a client right now because I don’t want to constantly refactor it over the coming months as Gutenberg itself changes.

      If you want to build a custom block right now, I think ACF is the best option. 3-6 months from now, who knows tools will be available.

      I also assume ACF will soon support saving attributes as metadata rather than in the post content. That’s really the core of what ACF is, so I’m surprised it’s not in the first beta releases.

      My bigger concern is when you’re working with other plugins that add their own blocks and you want to access their data, but they didn’t store it as meta. The Gutenberg Block Object plugin will be useful here. It stores all the blocks and their attributes as post meta. You can then access the post’s content in a more structured manner.

      As I said up in the post, I still lean towards not using Gutenberg unless we think it’s worthwhile for this project. A project with a lot of structured data would likely be a better fit for Classic Editor + Metaboxes than Gutenberg currently.

Leave a comment