I’ve been critical of WordPress’s new Gutenberg editor since day one, primarily because of its “HTML+comments as structure” language, and I’ve blogged before about the importance of context-aware rendering of blocks, but with the complete lack of complex block layouts in the initial set of beta Gutenberg blocks, It was hard to show people a hard and fast example of how HTML as structure is insufficient in the real world.
Then the columns block was released.
You see, columns aren’t trivial. Everyone understands the concept of columns. They are a set of content blocks rendered side by side at varying widths, that contain other blocks. However, there are three competing methods of creating columns in HTML:
- Row/Column – This is the only form of column supported by older browsers, including IE11. It is also the method favored by popular frameworks like Bootstrap, Foundation, and Semantic UI. Each set of blocks that makes up a column is wrapped in a single column element, and each set of columns is wrapped in a row element. Both traditional float-based columns and flex-based columns work this way.
- CSS Grid – This is the new kid on the block, and it removes the need for the column element, as each element can declare its column independently through css. It is considered slightly more semantically pure, but is only supported by the newest editions of popular browsers.
- Tables – This is not considered acceptable HTML for modern websites, but is standard practice for responsive emails, where neither Row/Column nor CSS Grid are well supported.
So, what’s the problem?
Gutenberg’s column block chose to use CSS Grid to render its columns. Which makes posts created with this block incompatible with IE11 and earlier browsers. And since Gutenberg stores its content as HTML+Comments, it’s pretty hard to change that. How hard? Let’s give it a shot!
Ok, let’s put together a test case:
- First off, I’m gonna reject the option of replacing the column block with a 3rd-party column block. As a smart developer, I want my site to be able to one day use CSS Grid, when the clients’ browser share will let me, and I want to rely on layout tools that will be supported for the foreseeable future, so the core columns block is the way to go. Plus, Ideologically, a core columns block needs to canonically solve the problem of columns, not spawn hundreds of competing and incompatible column blocks. Columns also need to be portable from theme to theme, so that we don’t have visual-composer-esque vendor lock in, and row/column vs grid should definitely be a theme choice.
Given these limitations, what I need is to be able to save the post using WordPress’s default columns block, then render it as row/columns elements rather than as grid. This same issue would appear if I wanted to format a custom feed to send to Mailchimp or Sendgrid, so I could use email-compatible column layouts. It even applies to other blocks, if I want to add additional semantic markup to support specific stylistic effects.
Put simply, if blocks are structured data, I need to be able to filter a block’s output with PHP.
Only one problem there.. Columns are a static block, and Gutenberg doesn’t parse static blocks on page load. In fact, Gutenberg is pretty light on PHP hooks, in general. It certainly shows where the focus of the project was.
Ok, so I need a way to get around this limitation. Luckily, Gutenberg does parse dynamic blocks on page load… kinda. Despite all the fancy talk about a formal grammar and the famed PEG parser, the
do_blocks function that is attached to
the_content filter and parses dynamic blocks, Gutenberg still parses blocks out of HTML with regular expressions. Which is BAD. I’m guessing this was done because of speed concerns with the PEG parser, but it really destroys any semblance of a common, formal grammar.
The good news is that the
WP_Block_Type class just checks to see if the public property
render_callback is callable to decide if a block is dynamic or not. That might give us options.
Ok, so this is getting super hacky for what should be a simple task, but i suppose I could grab the
WP_Block_Type object that represents the columns block, after it’s registered, and then attach my own callback function to
render_callback, so I can filter the HTML output.
Ok, so lets take a look at what we get in the
Just block attributes?? No way to access to the block’s content at all? WHY? The unexpected regex even clearly makes allowance for non-self-closing dynamic blocks (albeit in a way that would break self-nested dynamic blocks), but that data is never passed on to the render function!
Furthermore, even if the developers replaced the regex with the proper PEG parser, It still only returns the block’s content as an undifferentiated blob of HTML, so I would have to run the PEG parser a second time on the contents of the column block, so I could get the attributes for each nested block, just to render wrapping column elements. Of course, that ignores the possibility of sub-nesting blocks, so I would actually need to run my parsing function recursively… ugh.
At this point, my only option would be to build my own custom parser that runs on the filter
the_content, and parse the raw HTML independently, with a custom DOM walker. This is apparently the power of structured posts in action!
Now, the logical rebuttal to all this is “columns are experimental, and just a tech demo for nested blocks. Nested blocks will be a focus after the initial release”. But that is my entire point. Columns are an effective bellwether for issues with filtering output and with nested blocks. The core team keeps saying Columns and nested blocks will receive attention when the customizer focus occurs after the initial Gutenberg release, but these issues are fundamental, and pose serious issues to the flexibility of Gutenberg. If they aren’t given attention and fixed before the API is finalized and the first version is released, they will be extremely hard to address while maintaining backwards compatibility.
Gutenberg isn’t a post editor. It’s the core ideology for the new WordPress dashboard. Because of that the complex edge cases are crucial to address before release.
Right now, Gutenberg blends structured data and semantic output into one undifferentiated blob. That is a terrible path forward for WordPress.