Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce concept of block templates for full site editing #17512

Open
felixarntz opened this issue Sep 22, 2019 · 15 comments

Comments

@felixarntz
Copy link
Member

@felixarntz felixarntz commented Sep 22, 2019

Based on ideas previously explored in #3588 (and PR #4659) and recently discussed via @mtias in https://make.wordpress.org/core/2019/09/05/defining-content-block-areas/ (see also particularly this comment by @westonruter), let's further specify how today's paradigm of hard-coded theme template files can transition to enable full-site editing using blocks.

There are two major technical pieces needed for this:

  1. An infrastructure to store blocks outside regular post content (in "block templates") and load these templates based on the current context (e.g. query variables and the main WP_Query instance). This issue is focused on introducing said infrastructure.
  2. Block types to cover the minimum requirements, both functionally and semantically, to cover what is currently handled by theme template files. These necessary block types are discussed via #15623 (and explored in PRs like #17207 and #17263).

Based on these two pieces, several UI decisions need to be made on how to expose these features to the user, and we will likely need some iterations to get this right. While we'll out of necessity probably touch on some of those ideas here, I suggest we focus on finding a solid under-the-hood solution first.

Here's the concrete suggestion:

  • Introduce a post type wp_template, of which each post represents a single top-level template. In other words, a template that renders an entire page's markup (to be differentiated from so-called template parts).
  • Fully maintain the existing template hierarchy, which is currently used to determine which theme /*.php template file to load. Basically, each wp_template post represents a specific one of those template files.
  • Provide a basic skeleton taking care of printing the non-visual markup (<html>, <head>, wp_head hook, <body> opening and closing tag, etc.). Visually, it will render the actual blocks from the current template.
  • Skip the templates provided by themes entirely, since a hybrid approach would present unnecessary obstacles and confuse the user. Since this will drastically change how themes need to be developed, let's put it behind an experimental flag for now. Eventually, it could possibly become something that requires explicit add_theme_support(), at least in a transitional phase.

Based on this, a few further thoughts:

  • We will also need a way to reuse certain "template parts". This could be accomplished with e.g. the existing reusable blocks feature, or a separate post type wp_template_part, or an alternative variant of reusable blocks that only applies to template content (i.e. not individual post content, for better separation).
  • WordPress theme developers are typically familiar with the template hierarchy, and thus will easily be able to understand the block-based version of it. Given that the necessary block types are present, porting existing templates over will be trivial.
  • For the eventual UX, we probably should not expose template identifiers (such as index, category, singular, single-product, etc.) to the user because these are rather technical. While every theme developer has been exposed to them, they have (for a reason) never been used in WordPress UI. We will need to find a solid way for a user to understand which template they are editing, and for them to decide in which context/scope they'd like to edit the template. For an initial (experimental) implementation, it's fine to just expose block templates with these names.
  • We also need to think about how themes can provide default block templates. A theme could for example provide general layouts to choose from when creating a new wp_template post. Or it could include default block content (in place of today's template files) to use even if no user-created variant of that template exists as a wp_template post. Or on theme activation, these wp_template posts could be automatically created.
  • Developing a theme will be greatly simplified by this methodology. A theme will be responsible for the style and default layouts/templates, and most likely also it will still control some level of user-facing customization options (whether using the Customizer or an alternative interface). However, several other theme features today will become obsolete. In a block-based world, something like custom-header, widget areas, or nav menu locations probably don't make sense anymore. Other "features" like adding theme support for title-tag could potentially be automatically enabled.
@epiqueras

This comment has been minimized.

Copy link
Contributor

@epiqueras epiqueras commented Sep 27, 2019

We will also need a way to reuse certain "template parts". This could be accomplished with e.g. the existing reusable blocks feature, or a separate post type wp_template_part, or an alternative variant of reusable blocks that only applies to template content (i.e. not individual post content, for better separation).

I'd say a separate post type and block for template parts makes sense. The design will probably differ a lot from reusable blocks so it will be better to keep the code separated early on.

We also need to think about how themes can provide default block templates. A theme could for example provide general layouts to choose from when creating a new wp_template post. Or it could include default block content (in place of today's template files) to use even if no user-created variant of that template exists as a wp_template post. Or on theme activation, these wp_template posts could be automatically created.

See the patterns proposal: #17335.

@westonruter

This comment has been minimized.

Copy link
Member

@westonruter westonruter commented Sep 28, 2019

We may want to consider keeping track of which theme was active when a given wp_template post was created. While ideally all themes that support wp_template should be able to simply swap out the template, some themes may have stylesheets that expect certain template block configurations. If the wp_template posts were tagged for the active theme, then it could provide a way to filter the templates based on this. This comes to mind based on the Additional CSS feature in the Customizer, which stores CSS in a custom_css post type, but the CSS is theme-specific so each theme gets their own dedicated custom_css post. We were thinking at some point to provide a way to import CSS from one theme into another, and a similar idea may apply here for templates. In any case, the default case for wp_template posts is that they should persist by default when switching themes.

@westonruter

This comment has been minimized.

Copy link
Member

@westonruter westonruter commented Sep 28, 2019

  • We also need to think about how themes can provide default block templates. A theme could for example provide general layouts to choose from when creating a new wp_template post. Or it could include default block content (in place of today's template files) to use even if no user-created variant of that template exists as a wp_template post. Or on theme activation, these wp_template posts could be automatically created.

Yes, a theme should provide an templates block data that get merged with wp_template posts into the template hierarchy. Whatever is defined in wp_template posts should override what is defined in the theme. As soon as a wp_template is removed, then the theme-provided template data should be used. I don't think the theme-provided template data should get inserted as wp_template posts upon activation.

When creating a wp_template post, then you could decide to use one of the theme-provided templates block data as the starting point. Something like what @jeremyfelt shows in a tweet.

@noahtallen

This comment has been minimized.

Copy link
Contributor

@noahtallen noahtallen commented Oct 1, 2019

When creating a wp_template post, then you could decide to use one of the theme-provided templates block data as the starting point.

I think this is definitely a good idea - regardless of the current content of a template part, the user should be able to choose the theme's default content for that area. I think users are often confused towards why the theme's screenshot or demo looks different from their site when they activate their theme, and this would help with that.

Perhaps there could be a way for the user to choose, on theme activation, to use the default content from the theme.

I don't think the theme-provided template data should get inserted as wp_template posts upon activation.

What if a theme defines another content area for which the user doesn't have a template part yet? I can see a case for auto-populating the default content for that area in this case.

@westonruter

This comment has been minimized.

Copy link
Member

@westonruter westonruter commented Oct 1, 2019

What if a theme defines another content area for which the user doesn't have a template part yet? I can see a case for auto-populating the default content for that area in this case.

Yes, in this case I believe the template part can be populated directly with data from the theme (e.g. in JSON), without needing to actually create a wp_template_part post in the DB.

@vindl

This comment has been minimized.

Copy link
Member

@vindl vindl commented Oct 21, 2019

Since we already did some Full Site Editing work on WordPress.com, I’ll try to convey some of the things we’ve tried so far and some of our takeaways. I’d like to preface that with a comment that our work was intentionally scoped down for two reasons:

  • We wanted to have a smaller proof-of-concept solution that we could test with our users and gather feedback that would inform our future work (and possibly Core).
  • We decided to develop this as a plugin, which posed additional limitations, since this work ultimately requires Core changes.

During our work we experimented with a couple of approaches:

  1. Hijacking the template hierarchy from the plugin.

    • Pro: it works with existing themes without needing to modify them.
    • Con: imposing one template for all themes introduces visual and structural inconsistencies.
    • Note: we also looked into overriding get_template_part, but in case of Header it pulls in <head> tag too, and not just <header> content.

  1. “Blank theme” approach - essentially a theme with only index.php defined that replicates the template hierarchy using predefined page templates (in wp_template CPTs).

    • Pro: allows the most freedom when editing page templates in Gutenberg and assigning them to specific posts/categories.
    • Con: backward incompatible with existing themes.
    • Note: this also redefines the concept and area of responsibility of future themes. For more details check out this exploration from @Copons: Automattic/wp-calypso#32865

  1. Adapting existing themes to make them FSE-aware. If they detect that FSE is active, they would delegate template part rendering to it, otherwise they fall back to their default non-FSE content.

    • Pro: can be made to work with existing themes.
    • Con: themes have to be updated to make them FSE aware; introduces slight coupling between themes and FSE functionality.

We ended up using (3) in the end, because it seemed like the most seamless way to get what we need for our scoped-down solution. While this worked for us, it’s not necessarily the best long-term approach for Core, but some pieces could still be reused. This code now lives in:

Populating and storing template data

Our initial template data population happens on after_switch_theme hook, and this can work regardless of the final data storing approach. It provides a way for each theme to register its own specific template data. In order to avoid duplicating this code across all themes that want to support FSE, this functionality is now implemented in FSE plugin, but it seems like something that could be incorporated into core down the road. As a side note, we also attempt this on plugin activation, to cover the case when FSE supporting theme is already active. Overall it follows these steps:

  1. Data population executes on after_switch_theme or register_activation_hook.
  2. We check if template data for this particular theme is already present and bail if it is.
  3. Fetch template data for the theme being activated from a centralized API.
  4. Populate corresponding template CPTs with fetched data.
  5. Mark CPTs with corresponding theme specific taxonomy terms (e.g. maywood_header or similar).

In our case the benefit of (3) is that we don’t have to release a new theme version if we want to publish a fix or update to template data.

As a result of (2) and (5), it’s possible to preserve existing customizations to template parts across theme switches. For example, if you want to just try out a couple of new themes on your site, and then revert to your original one, the previous theme specific customizations to your header would be restored.

For storing the data, we are now only using wp_template_part CPT, but that’s just because our solution didn’t require more. When we tried to envision what core is going to do and align with that a couple of months ago, we leaned toward the wp_template and wp_template_part split, as opposed to just wp_template CPT with hierarchical taxonomies. To clarify, both options seem viable from the implementation standpoint, but it seems like the former requires less wiring, might perform slightly better, and could better aligns with existing WP concepts and infrastructure. Some benefits in my opinion:

  1. There is a nice parallel with terminology in existing template hierarchy (page templates and template parts).
  2. We are trying to model a tree structure, and that’s easier to enforce with the above split. In order to prevent it from becoming a cyclic graph, that could result in infinite loops in editing and rendering context, our query would have to be more complex and filter out page templates in some contexts (e.g. we want to prevent template A containing template B, which also contains A).
  3. Similar to (2), designating a template part as a full page template wouldn’t make much sense, again requiring additional code, and likely another table join with taxonomies to filter them out (depending on implementation). For example: assigning the Header template part as the Homepage template doesn't make sense because a "full-fledged template" would entail several template parts (header, footer, post content etc.).

While all of the above can easily be worked around, they might hint that we are trying to clump together two conceptually different entities under the same roof, and we might discover additional work on account of that down the road too.

It seems to me like the model would become much simpler if we enforced no nesting of entities of the same type, both for wp_template and wp_template_part. So page template could only contain template parts, and template parts would in turn be composed of static, dynamic, and reusable blocks. I’m still not sure if this is too constraining, and it definitely requires some more thought. So something along the following lines:

FSE templates (4)

P.S.: I tried to condense this as much as possible, but it still turned out a bit longer than expected - sorry about that. :) If anything is unclear because of that I'd be happy to clarify. Also, if there is any way that we can further help the core FSE work based on what we currently have on WordPress.com (be it feature testing or user feedback) feel free to let me know.

Kudos to @Copons for reviewing this and suggesting edits. ❤

@epiqueras

This comment has been minimized.

Copy link
Contributor

@epiqueras epiqueras commented Oct 21, 2019

Thanks for writing this up @vindl!

During our work we experimented with a couple of approaches:

I think that a mixture of approach 1 and 2 would be optimal here. Themes should register template posts that hijack the hierarchy, but they should also be able to manually include them in PHP files. We're starting with a limited version of the former here: #17626, and of course the manual approach is always possible by including the CPT posts with custom code, but we might want to expose an explicit API for it in the future, although I also see value in just making the hijack the "supported" approach.

As a result of (2) and (5), it’s possible to preserve existing customizations to template parts across theme switches.

This is something we need to do as well.

It seems to me like the model would become much simpler if we enforced no nesting of entities of the same type, both for wp_template and wp_template_part. So page template could only contain template parts, and template parts would in turn be composed of static, dynamic, and reusable blocks.

I agree, this approach also makes the most sense to me.

@Copons

This comment has been minimized.

Copy link
Contributor

@Copons Copons commented Oct 22, 2019

If I may chime in, as a person who worked hard on the WordPress.com Blank Theme experiment, and struggled immensely in explaining it to peers and stakeholders alike: we desperately need the best and clearest terminology we can come up with.

wp_template, wp_template_part, template hierarchy, template files: it's painfully obvious to me that the "template" word is overloaded of meanings!

I don't have a good answer, and I know this seems like bikeshedding, but if we manage to figure out some unambiguous terms, then FSE will become 100% easier to discuss, and the project itself will move forward quicker.

@epiqueras

This comment has been minimized.

Copy link
Contributor

@epiqueras epiqueras commented Oct 22, 2019

How about:

wp_templates: The new "Template Posts".
wp_template_parts: The new "Template Part Posts".
Template Hierarchy: The classic WP template hierarchy.
Template Files: The classic .php template files.

@Copons

This comment has been minimized.

Copy link
Contributor

@Copons Copons commented Oct 22, 2019

How about:

wp_templates: The new "Template Posts".
wp_template_parts: The new "Template Part Posts".
Template Hierarchy: The classic WP template hierarchy.
Template Files: The classic .php template files.

I think I was misunderstood: the meaning of each term is clear when you list them in a glossary.
But I promise you: casually mentioning, for example, wp_template in a conversation about Full Site Editing will create major confusion and questions, depending on the level of familiarity of the people involved. 😅

In the end, it was inevitable to me to alwayt add a bit of description and details every time I mentioned a %template% term for the first time in a post or comment, which in turn makes everything else less clear and harder to read and follow.

@epiqueras

This comment has been minimized.

Copy link
Contributor

@epiqueras epiqueras commented Oct 22, 2019

We should just link to a glossary.

@noahtallen

This comment has been minimized.

Copy link
Contributor

@noahtallen noahtallen commented Oct 22, 2019

@epiqueras

This comment has been minimized.

Copy link
Contributor

@epiqueras epiqueras commented Oct 22, 2019

• The block template API against which blocks are validated. (I.e. inserted blocks can be required to match the global block template and some other block components include templates)

Those are called block templates.

• Starter page templates: while not in core per se, this is the case where blocks are auto-populated into a page/block area.

Those are now being called block patterns.

Haha, yeah this will get confusing.

@koke

This comment has been minimized.

Copy link
Contributor

@koke koke commented Oct 23, 2019

Those are now being called block patterns.

At least for mobile we are calling them starter page templates for now (see #18055).

Based on the conversation in #17335, I understand block patterns are meant to be parts that get inserted into a page rather than a full page template to get started.

@epiqueras

This comment has been minimized.

Copy link
Contributor

@epiqueras epiqueras commented Oct 23, 2019

Based on the conversation in #17335, I understand block patterns are meant to be parts that get inserted into a page rather than a full page template to get started.

A full page template can just be a pattern that gets applied automatically. Even if we decide to name them differently in the UI, I think they should share the same implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
8 participants
You can’t perform that action at this time.