I’m kicking off another larger/quasi-enterprise website build and am planning on going full steam on a block-based theme again. My plan with this post is to outline the main developer experience issues I know that I’ll be facing, with some thoughts around how I plan to solve them, including what tools I might need to leverage along the way. This post is sort of an update to an earlier post: Thoughts on Version Control for Block Themes.
Typically at the beginning of a project, we’ll outline the technical specs, like what custom features, post types, integrations, blocks, etc we might need. This is just focused on full site editing developer workflows. In the era of classic themes, you could really build an amazing and lean WordPress site with just ACF and a good starter theme. With FSE, it’s a lot harder to resist the urge to add low-install-count plugins or just give up and use a page builder. Hopefully more open discussions like this will help us solve these issues.
If you’re building similar block-based sites, please connect with me so we can share resources. There’s a group you can join in the Make WordPress Slack #outreach
channel along with a few GitHub discussions, including this one on Version Control.
Contents
Base Theme & Assets
I have a starter FSE theme that a colleague and I have been working on that is pretty great to work with. The key differentiator is that the theme is for developers with a build process built on top of @wordpress/scripts
.
It also includes scaffolds for adding custom settings to the block editor, custom blocks, core block extensions, and selectively enqueueing block-specific stylesheets without having to write almost any extra code. As much as possible it relies on using theme.json
and the corresponding CSS variables when CSS is needed.
The theme itself is completely un-opinionated in terms of design (this is not a starter theme like Ollie or Rockbase) and is sort of like the _s
for full site editing. There’s a bit of real-world testing that’s needed, so happy to take it for another test drive.
Frontend JavaScript Framework
I like having a bit of structure for my frontend JavaScript, especially when building custom interactive blocks like tabs or carousels. I don’t want to go back to jQuery but it’s really nice having some basic structure. Last time around Alpine.js was perfect for that. Having experimented a bit with the Interactivity API recently (which has a similar approach of custom directives mixed into your HTML), I think that could be a fun way to go.
This will work if I don’t need anything complicated, like custom stores for example. The way it uses JavaScript Modules means it won’t work with the current build process (commands require the --experimental-modules
flag currently, which then seems to break our custom webpack config for multiple entrypoints).
If I need to write actual JavaScript for it, I guess I could write it outside the build step or just make any interactive blocks in a separate plugin directory.
Templates & Template Parts
This is where we move into one of three major workflow gaps in block themes, starting with – how do I control the design of the site once content has been entered in? Templates/template parts can be easier, assuming nothing needs to be edited on the live site. Meaning you can completely version control them if you make a few modifications:
- Templates (i.e. single, archive) can be exported from the site editor to the theme using Create Block Theme. As long as the templates are never touched on the production site, changes can be pushed from local to prod pretty easily. No editing your 404 page or archive templates on prod!
- Template Parts (i.e. header, footer) can also be exported, but its worth noting that CBT converts them to Patterns first (which does allow them to be stored as PHP with image assets added to the theme by default). But then the minute they’re edited on the production site, they “break” the connection to the pattern.
I’m not really worried about templates as my strategy is basically to hide the entire site editor from the client. That said, I miss the concept of “Block Template Parts” from hybrid themes – it was an easy way to curate a couple block editor “chunks” without opening up the entire site editor. (I might consider doing something with a CPT if I need to give them access to alter any content that’s inside a template, say a Blog hero area).
The template parts (in this case, the header and footer) can probably remain untouched by the client. The only piece they’d need to modify might be menus or the social icons. There’s also a few links inside them (i.e. Privacy Policy, Terms of Service) so maybe those can be menus as well. I’ll talk about my solution for menus below.
For social icons, if they actually need to be able to edit these, I can control them with a reusable block or a filter. That said, that’s a low priority.
Synced Patterns/Overrides
I’m treating patterns as a completely separate topic from templates: Block Patterns are not exported into your theme by CBT. There’s always Pattern Manager, but it’s abandoned and has been slowly falling apart with each new core release (the inability to easily group blocks just kills me).
The big tradeoff is always: how can I control some elements of a pattern’s design in the code (say spacing and overall layout) but other parts (content, colors) per instance?
The end goal would be partially-synced patterns/pattern overrides, but not only is that not ready for 6.5, I have serious doubts that an ideal user experience will get fully realized in the next year when ideas like forcing users to type into text input fields in the sidebar to edit their page content are being considered. And there’s a lot of underlying work that needs more thought.
Potential approaches to patterns
Add a class to a pattern’s outer element – then you just override things with CSS. The downside is the styles you see in the inspector controls might be wrong. That said, I think this is probably one of the quickest/most common approaches I see.
I could use ACF Blocks in place of patterns, but then that starts to remove a lot of the benefit of the block editor and you’re constantly making trade-offs. ACF Blocks are really useful at the block level, but not at the larger ‘pattern’ level, and editing input fields in the sidebar or a form is sort of a ‘last resort’ approach.
Custom dynamic blocks are comparable to the ACF Blocks approach – helpful at the individual block level. A good example of this might be Rich Tabor’s Cards block. Sure you could build this with core blocks, but Rich makes a good point that this stripped down dynamic block is a cleaner user experience. I’ll probably have a few of these for elements like icon lists and cards, but they don’t solve the larger pattern issue.
Patterns with contentOnly Locking
The approach I’m leaning towards is using block patterns but setting contentOnly
locking to them. The structure would essentially be an outer group
block where you can override the background/text color and overall spacing. Then inside that another group
block with the contentOnly
locking enabled- then the rest of the pattern is inside, where you’ll only be able to edit text, images, buttons, etc.
I’ve created a variation of the group
block to scaffold this structure that essentially looks like this:
{
name: "content-locked",
title: "Content Locked",
description: "Group block with locked content",
attributes: {
align: "full",
tagName: "section",
className: "pattern--",
layout: { type: "constrained" },
metadata: { name: "Pattern" },
},
innerBlocks: [
[
"core/group",
{ templateLock: "contentOnly" },
[["core/paragraph", { placeholder: "Content locked" }]],
],
],
},
With this variation, there’s now a “Content Locked” template I can insert that has everything I need to scaffold a block pattern.
The contentOnly
locking works well enough. You can’t edit anything but text/images and you can’t even select blocks that aren’t editable, so all the groups and columns sort of disappear. Users can also leave content or images empty and for the most part, those blocks won’t show up on the front end.
Where it breaks down is the idea of repeatable inner blocks. For example, once locked, you can’t add more buttons to a buttons
block or more list items to a list
block. You can’t take a three-column or grid layout and duplicate or remove columns. In that sense it harkens back to the ACF Flexible Content days – way too rigid. For a few of my designs, I might actually need to make small custom blocks to work in this mode.
That balance between flexibility and stability is essentially the hard problem the core team will need to solve. And I guess for power users, you can actually just “unlock” a contentOnly
locked block in the block editor and edit anything anyway (yep).
The downside of all this is that it doesn’t really solve version control and global updates to the attributes of blocks inside the pattern. For example – if I add a default amount of spacing or color on a block but want to change it globally, I would need just hard-code some CSS over it. But… I don’t really see another option without pattern overrides in core. And because of the nature of patterns, this CSS will have to be loaded globally (although I do have an idea for better block styles management…).
For now, block patterns are really serving as wireframes, and as long as the structure is solid I can tweak the rest later. The end user doesn’t see any settings on those innerblocks, so they don’t notice/care if I’m overriding it with CSS.
Patterns in Themes
The last piece is getting block patterns from WordPress into your theme for version control. And then still being able to edit a pattern in the site editor once it’s saved in your theme. Obviously those were the exact two use cases that Pattern Manager excelled at. The goal would be for Create Block Theme to solve this problem, maybe by looking at Pattern Manager and taking what works.
In the meantime maybe the best option is to just keep using Pattern Manager. If I had extra time, I could fork Pattern Manager and at least fix the “grouping” issue and any other major problems.
(I did do some experiments with a custom “Pattern” block that would automate some of this contentOnly
locking work and pull the patterns directly from your theme, but I ended up just getting too deep in the weeds on it and it felt too fragile an approach.)
Responsive Design
Responsive design in the block editor is rough. In the past, I’ve included custom block-level responsive design controls in projects as a way to get around this. An example is adding a small “Reverse on Mobile” checkbox to Columns or Row blocks.
If I end up going the approach above of tightly controlling all of my patterns anyway, then I’m less worried about responsive design, because I can just code it in with CSS. I’m not even sure I’ll need to add any custom settings, though a “center text on mobile” checkbox will probably end up making it in.
That said, this design really has no negative margins, no crazy mobile stuff. I’m getting lucky that the “intrinsic” design approach will mostly be fine here.
Except of course for… the mega menu.
Navigation Block
Nobody’s best friend. The navigation block. Where do we start?
To me, the lack of support for menu “locations” in the core Navigation block is a criminal offense. It essentially means that any templates / template parts with menus in them cannot be version controlled at all. This is… unacceptable? How many headers and footers don’t have any navigation menus in them? It also makes switching themes much more tedious than it needs to be.
To make matters worse, the header design I have is basically ‘mega menu’ territory.
For the footer and top-header menus, I was going to try Jonny Harris’s Classic Menu Block. Unfortunately, it doesn’t solve the one issue I want it to solve – fetch a menu by its location, not an ID via the database. So… I’m probably just going to fork his plugin and see if we can store the menu location instead of the menu itself. Looks doable. And maybe add in block style for some horizontal menus.
For the header, I’m going to try something similar to Nick Diego’s concept of a mega menu block, but potentially with a bit of the complexity stripped out, mainly because my users will never need to edit it. Each dropdown in the mega menu will be a block template part made up with Classic Menu Blocks. My clients will probably be editing classic menus for everything, with pretty much no control over the design.
So… no navigation block if I can help it.
Conclusion
There’s plenty of other decisions that go into building a site like this. A few quick examples from this project:
-
theme.json
and reminding myself how to style basic elements like the separator and bullet lists. - the use of SVG Icons and a custom add-on plugin I had to build for Nick Diego’s The Icon Block.
- the unique hosting/deployment situation.
- a Salesforce integration and the ability to personalize content based on referral info.
- and then of course the client, with their deadlines and bureaucracy and other incentives.
There was a solid decade of agency life where a lot of these workflow questions were answered, where there were best practices and courses and tutorials and major theme frameworks dedicated to solving these issues. And there are still plenty of other options (like Sage or TailPress) that are pushing the boundaries of modern WordPress in completely different ways.
I would love to just build a website on core WordPress, offer a solid visual editing experience, and not break my brain trying to figure out how to control everything in a nice git repo. I’m making progress with each project- with help from other community members sharing their ideas- and hoping that after this build I’ll have a few more answers, and that core will have made a few more steps towards offering a decent developer experience. I’m hoping that thinking aloud like this helps.
To repeat what I said above: If you’re building similar block-based sites, please connect with me so we can share resources. Let me know if you see any holes in my strategy or want to point me towards anything else useful.
Leave a Reply