WordPress/gutenberg

The `wp:pattern` block

mtias opened this issue · 19 comments

mtias commented

Pattern block tracking issue - #50159

We've so far treated patterns as a block instantiation feature but not a permanent object. This was a useful lens with which to introduce patterns and enabled many workflows (like copy and paste from the directory) without adding further edges to the block API. With time, we also introduced a utility block called wp:pattern to allow block theme authors to reference individually registered patterns dynamically, which has also helped with translations.

There are a series of upcoming features around #39281 we want to unlock that now require us to look more seriously into making this block fully powered:

  • Being able to designate a container that can cycle patterns within — just like template parts but without the extra overhead they entail. #44581
  • Introducing patterns where the content is configurable on a per-instance case but the design and layout is shared and can be globally updated across instances. This requires developing the contentOnly property further to establish a set of attributes (role == content) that the pattern is then injected with dynamically.
  • Helping the zoomed.out view properly focus on patterns rather than top level blocks.
  • Possibly unifying concepts with reusable blocks where everything is a "pattern" just with different sync properties.
  • Attach theme.json child objects to define a new set of style properties that only apply to the pattern contained within.

All of these features can be better conceptualized with the help of such a block.

Task list:

+1. Ever since the pre-block era, I've wanted an instantiable thing in between patterns and custom (innner-enabled) blocks.

There were whole communities around "parts-based" classic themes using cruder tools of the day, and widget architecture was never quite enough.

I'd like a souped-up wp:pattern to support a Javascript hook (preferably ESM enabled), sort of like what was added to block.json. Would open many options and DX improvement too.

I dabbled with a solution to this but didn't quite get there.

I tried creating a post type for creating layouts. In these layouts I would add some layout blocks and some content. I would also add a custom "slot" block wherever I wanted to be able to add editable content when using the layout.

The idea is this:

  1. Create layout, with locked content/layout blocks and slot blocks for editable content
  2. Insert layout into the block editor
  3. Everything in the layout is locked, except for the slot blocks which work like InnerBlocks areas. In these slots any other blocks can be inserted.
  4. If a change is made to the layout, this change affects every instance of the layout wherever it is on the site.

The layout works like the wp:pattern block. It server side renders all of the rendered html and creates InnerBlocks areas wherever there are slot blocks.

Imagine a block version of get_template_part. That's what I'm longing for!

@mfinell This is exactly the missing link of what is needed (IMHO). I'd love see this in action :)

@mfinell There was a good example of your "slot" idea reported at #50060. Did you end up implementing it, or is it just an idea?

@talldan, thank you so much for opening the tracking issue at #50159. I wanted to share a few initial thoughts here as there are a few things you need to take into account when thinking about the next steps.

Reusable block, as we know it today, allows any type of editing for inner blocks. I assume it’s what we will refer to as a “fully synced” block pattern in the future.

We discuss introducing a local block pattern (explored in #49607) stored internally as the Reusable block, and the design aspect is going to be synced in all places on the site. The content is going to be treated as placeholders/default states. I bet it’s what we will call a “synced” pattern.

Technically there is no difference between the two use cases I mentioned when you are creating/designing the pattern for the first time (or editing as admin/designer). It’s the same experience. The only difference when using those two block pattern variations is that one allows editing everything, and the other can have only the content updated (contentOnly lock mode).

What’s important here is the last remaining use case, which I see referred to as an “instance” pattern. It’s where we represent the block as a pair of attributes: the name (slug seems to be a good fit) of the “synced” pattern and the content entered by the user. If we were to use the Reusable block to represent it, we need to keep in mind that it’s going to reference another Reusable block that holds the design of the block.

At least for me, it would help to see some visuals of how all those options for block patterns would get exposed to the user and how they would navigate between them.

Some more thoughts about the suggested approach on this closed experimental PR

To bring more visibility to the discussion we had on that closed PR, I'm duplicating some of my thoughts to centralize the discussion in this issue.

First thing that I mentioned as that I think we should probably leave the current core/pattern block untouched. I also don't see a path forward for them without breaking changes to live sites. (adding a wrapper)

Also based on the use-cases mentioned above that we want to unlock:

  • we want to be able to save patterns ultimately like we save reusable blocks, so even patterns could use the database as a source and not the registry.
  • we want different ways of "syncing" blocks: full synced like reusable blocks, no syncing, partial syncing (everything but the role content stuff)
  • Ultimately align all these blocks (patterns, reusable blocks, template parts)

My suggestion was that we should initially focus on introducing the behavior changes: syncing, shuffling... while using the existing reusable block because we do want to source patterns from the database, from the wp_block CPT just like any reusable block, the only difference is that we want to be able to define how to "sync" with the referenced reusable block.

So as a first step, I think we should explore adding a new attribute to the core/block: syncStrategy (or something like that) to define how the core/block instance synchronize its content with the reference (saved pattern/block).

There are potential follow-ups we should consider:

  • Allow tagging, or saving the "category" of a wp_block CPT record in order to be able to shuffle between similar blocks/patterns (explore other ways to save semantics)
  • Whether we should use "slug" instead of "id" in core/block (to absorb the template part and/or pattern registry use-case)

If it helps we can also use a feature flag as we implement these new features.

I'm adding the first item there as the initial todo item to the issue.

pattern-syncing.mp4

Plenty of explorations around patterns in the past. Here's a more up to date pass and what pattern creation and management could look like when also considering syncing. The management side of this is a follow up #50397 and related to #50028

Nice, that looks good. I do think we still need a pass on the "Content" panel, across this, across #49980, and across the existing interface that ships with the contentOnly editing feature. Ultimately that panel could/should probably be the same as it is for the navigation inspector editing interface (right?). But that seems like something that can be explored sepaerately.

But focusing just on the key behavior, to me this seems ready to go, so moving it to the feedback stage now: are there any dev questions unanswered by this video?

Thank you Saxon!

The video above suggests that the "sync" attribute is something you define in the reference pattern while I think it's something that should be defined in the "instance".

Also, I think we should still keep the difference between "fully synced" and "partially synced", because that's a behavior that existing reusable blocks rely to and we can't just update it without notice.

@youknowriad can explain full vs partial syncing behaviour from a users perspective? What's the difference? I may not have a complete picture of existing experience.

Re sync attribute on reference vs instance. I was originally leaning towards the direction you suggest (prompting to sync on insertion) but the more I've thought about this the more I think that would be a difficult transition to make from the way reusable blocks and patterns currently work (UX not technically). I was hoping there would be a natural pathway towards that point if it makes sense in future.

@SaxonF The difference is that when "full sync" is applied, the entire pattern is used as is with no change, any change made to the pattern is also applied to all the instances that are also "full synced" including "content".

@youknowriad sorry I was referring to existing reusable block behaviour. We don't have partial syncing do we?

You could just lock all layers by default so the experience is reversed ie blocks are marked as overrideable by unlocking them

The difference is that when "full sync" is applied, the entire pattern is used as is with no change, any change made to the pattern is also applied to all the instances that are also "full synced" including "content".

A fully synced pattern is what we today call a Reusable block (in the future, this pattern could also be sourced from the Pattern Directory, the theme, or registered by the site). You can edit every aspect of the block in a single place, and it gets propagated in all places on the site (or sites for patterns from Pattern Directory).

A partially synced pattern is going to be a combination of the source pattern that provides the design. The design could be sourced from a static representation of the pattern from the Pattern Directory, from the theme, or from another fully synced pattern (Reusable block). The nuance here is that the user would be able to override locally the content displayed in the pattern. Still, all the changes applied to the design externally would get dynamically reflected on the site.

It's important to note that "locking editing" and "syncing" is not the same at all: locking only applies in the editor (in fact you can even edit everything in the pattern if you go to the code/HTML view) while syncing is about "rendering" (applies both in editor and in frontend).

The video above suggests that the "sync" attribute is something you define in the reference pattern while I think it's something that should be defined in the "instance".

I was personally imagining the opposite 😄

With the idea being that for editorial flows, a design team can create a pattern, and an author can fill it out without messing with the design. What are the use cases for the other way around?

It could also be both, the pattern creator can define what the inserter of the pattern can do, but maybe that's too complex.

You could just lock all layers by default so the experience is reversed ie blocks are marked as overrideable by unlocking them

I quite like this idea (although I haven't really thought about implementation 😆 ), kind of like the pattern would have locked/synced islands of blocks, but the rest is freeform.

It removes the simplicity of the editing experience (editing is the same as any other blocks rather than using content only mode), so I think we need more understanding of the motivation behind content only mode.

For me, content only mode is great as it makes the implementation of partial syncing much easier, but as Riad has mentioned it is really a separate concept to partial syncing, though one we lean on.

Great exploration. From an UI perspective I have only on concern so far. I'm not sure the 'Edit pattern' button shoul dbe placed within the Settings sidebar. The Settings sidebar should only contain complementari settings and secondary actions. All basic operations like editing should be available in the main interface. This is a principle that was established long time ago, for good reasons of usability and accessibility, and it's mentioned in a few places in the documentation as well, for example here and here. While the documentation mentions the Block settings sidebar, the general principle still applies here.

If I can offer two cents as an agency developer working daily on building Wordpress sites/custom themes for business clients:

  1. Firstly just to restate that this functionality is badly needed — it's wild to me that there is currently no “modern Wordpress” way available to a) combine existing/core blocks into a single pattern or block template, which then allows BOTH b) the content of each instance throughout the site to be user-editable AND c) the design/presentation to be “synced” i.e. updatable from one central location and applying to all instances of the pattern that have already been inserted/deployed throughout a site. This is such a core, ubiquitous feature used and needed by theme/website developers.
  2. As a subjective note, I think calling the states “fully synced” versus “synced” or “partially synced” is unnecessarily abstract — could the options just be called something more meaningful like “sync design only” versus “sync content and design” to make it more self-explanatory to users?

@wes-davis

  1. As a subjective note, I think calling the states “fully synced” versus “synced” or “partially synced” is unnecessarily abstract — could the options just be called something more meaningful like “sync design only” versus “sync content and design” to make it more self-explanatory to users?

+1 to this comment. The block editor is both used as a design tool and a writing tool. Global styles and theme.json help maintain consistency across a site but patterns don't offer anything in the way of design consistency. The more you use a pattern, the more places you need to go and manually update the pattern.

In most cases the site designer is concerned with updating styles and not content. The site editor/writer would care more about consistent messaging/content.