Skip to Main Content

Gutenberg Can’t Switch Semantics, and that’s a BIG Problem

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.

ADVERTISEMENT:

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:

  1. 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.
  2. 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.
  3. 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.
  • At the same time, I’m going to reject the idea of extending the column block’s JavaScript, for the same reason. When I eventually can allow my site to use CSS grid, I can’t do so in a way that requires manually editing the thousands of posts and pages I might generate before then, to support the column semantics that I need, and any of these extended column blocks will still not be compatible with the vanilla columns block, if I ever want to change back

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.

ADVERTISEMENT:

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 render_callback function..

ADVERTISEMENT:

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.

2 Comments

  1. Josh's profile image.

    Greg, thanks for following Gutenberg so closely. It’s good to get an objective and professional developer’s opinion on development progress.

    You said that Gutenberg does not filter static blocks. What kind of filters do we get for dynamic blocks, though? If I’m reading it right, it looks like custom dynamic blocks would call a developer-defined PHP callback, where we could theoretically put any filters we need. If that’s the case, I may be able to replace the ACF back-end for my own flexible content framework with Gutenberg, and keep all the PHP functions that handle markup generation in place.

    • gschoppe's profile image.

      Hi Josh,

      There aren’t currently any PHP filters on dynamic blocks either, just a call to the rendering callback function in the block’s definition. this lets you replace some forms of flexible content layouts with custom blocks, as long as each instance of the flexible content block is atomic (the blocks don’t have the ability to influence each other dynamically, at least not without a lot of hackery), and are not nested within dynamic blocks.

      The primary thing you can’t currently do with gutenberg, is have dynamic blocks with child blocks or have blocks with multiple sets of child blocks separated by HTML.This makes building complex repeater interfaces difficult.

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>