Skip to content

Block themes, a few years in: what I was wrong about

I resisted block themes for a long time and I was mostly wrong. Here is what changed my mind, and the two things I would still not hand to a client.

4 min read
Contents

I built my first block theme grudgingly, for a client who insisted. I had a decade of classic themes behind me, a Timber and Twig setup I liked, and a strong opinion that JSON was not a templating language.

Some of that opinion survived. Most of it did not.

What I got wrong

theme.json is the good part. I expected a configuration file that would fight me. What it actually is: one place that defines the design system and then enforces it everywhere — the editor, the front end, and the controls the client sees.

{
  "version": 3,
  "settings": {
    "color": {
      "custom": false,
      "palette": [
        { "slug": "ink", "color": "#16160f", "name": "Ink" },
        { "slug": "paper", "color": "#f6f4ef", "name": "Paper" },
        { "slug": "cobalt", "color": "#1a46e0", "name": "Cobalt" }
      ]
    },
    "typography": {
      "customFontSize": false,
      "fontSizes": [
        { "slug": "small", "size": "0.875rem", "name": "Small" },
        { "slug": "base", "size": "1rem", "name": "Base" },
        { "slug": "large", "size": "1.5rem", "name": "Large" }
      ]
    }
  }
}

"custom": false is the line that matters. It removes the colour picker. The client can use your three brand colours and nothing else, which is the thing every designer has wanted since custom fields were invented.

I used to enforce this with a page builder’s settings panel, badly, and it broke on every update. Now it is fifteen lines of JSON in version control.

Global styles beat a stylesheet for the things it covers. Spacing scale, typography scale, colour palette, block defaults — defined once, applied to core blocks I did not write and did not have to restyle. My theme CSS went from about 2,000 lines to under 400 on a comparable site.

Editing templates in the site editor is genuinely better for clients. Not for me. For the person who wants to move the newsletter box above the footer without emailing me. That is a real cost saving for them and one less “quick change” email for me.

What I was right about

The JSON is not a templating language and should not be treated as one. Block markup in HTML template files is not enjoyable to read or to diff:

<!-- wp:group {"style":{"spacing":{"padding":{"top":"var:preset|spacing|60"}}},"layout":{"type":"constrained"}} -->
<div class="wp-block-group" style="padding-top:var(--wp--preset--spacing--60)">

That is one wrapper. A page template is pages of it. Anything with real logic belongs in a block you wrote in PHP, not in markup composed in a visual editor and committed as a file nobody can review.

The editor still does not look like the front end. Better than it was, not the same. Every project still includes a round of “it looked different in the editor”, and every project still needs editor-style.css doing work it should not have to.

Version churn is real. Templates and block markup written two years ago need touching. Not breaking — WordPress is remarkably good about backwards compatibility — but deprecation notices accumulate, and the recommended way to do something moves. Budget for it on a long-lived site.

What I actually build now

A hybrid, and I think this is where most professional work has landed.

  • theme.json for the design system. Always. Even on a classic theme, where it still controls the editor palette.
  • Block templates for page structure. Header, footer, archive, single. The client can rearrange these and that is the point.
  • Custom blocks in PHP for anything with logic. A team grid that queries a custom post type, a pricing table, a form. These are render_callback blocks with an attributes schema, and they are ordinary PHP I can test.
  • Block patterns as the client-facing API. This is the part people skip and it is the highest-value piece.
register_block_pattern( 'acme/testimonial', [
    'title'      => 'Testimonial',
    'categories' => [ 'acme' ],
    'content'    => file_get_contents( __DIR__ . '/patterns/testimonial.html' ),
] );

A client who is handed twelve named patterns builds pages that look right. A client who is handed an empty canvas and a block inserter builds something you will be asked to fix. The patterns are the design system’s user interface, and they take an afternoon.

The two things I still would not hand over

Unrestricted full site editing on a site with more than one editor. Somebody will delete the footer template part on a Friday. There is no meaningful audit trail and no straightforward rollback. I give editors the pages and the patterns, and keep templates in the theme, in git.

A block-based checkout or anything money touches. Not because it cannot be done. Because “an editor can rearrange this” is an anti-feature on a flow where every element has a reason to be exactly where it is.

If you are still holding out

Build one. Not a client site — a small one of your own, with theme.json doing real work and two or three custom blocks in PHP.

The thing that changed my mind was not an argument. It was watching a client change their own homepage hero, correctly, without asking me, and realising I had been protecting a workflow that mostly protected my invoice.

Share