WordPress / Documentation-Issue-Tracker

Issue Tracker for the WordPress Documentation team.
https://make.wordpress.org/docs/
Other
84 stars 41 forks source link

Theme developer handbook: Add a dedicated page for patterns #342

Closed carolinan closed 2 years ago

carolinan commented 2 years ago

What is the new page you are requesting?

A dedicated page about how to register and use block patterns. A page that will refer back to the block editor handbook page at https://developer.wordpress.org/block-editor/reference-guides/block-api/block-patterns/

How will this new page help you?

Right now, it seems that information about block patterns in themes is spread out over several pages: https://developer.wordpress.org/themes/?s=block+patterns

Block patterns can be added in classic themes and block themes, but they can register them in different ways. Both the PHP function and the method with placing patterns inside the patterns folder should be covered.

For help with writing the content, you can refer to the dev notes: https://make.wordpress.org/core/2022/05/03/page-creation-patterns-in-wordpress-6-0/ https://make.wordpress.org/core/2022/05/02/new-features-for-working-with-patterns-and-themes-in-wordpress-6-0/

colorful-tones commented 2 years ago

[Draft] Register and use block patterns

Proposed areas to cover (unorganized):

Block patterns in themes

Block patterns are predefined block layouts. They offer a means to expedite and elevate the editorial experience by offering users a preset arrangement of blocks to start building right away.

Do you or your editors find themselves recreating patterns of blocks in the block editor? Then, converting those blocks into a pattern should save some time.

Let's jump in and learn how we can include them in a theme.

How to include block patterns

There are two methods for registering or including patterns in your theme and both approaches are compatible with classic and block themes:

Using the patterns/ directory to register patterns

You can add a new patterns/ directory within the root of your block theme and place PHP files within to register patterns. Each individual PHP file would contain a different pattern, e.g. patterns/my-fun-pattern.php, patterns/another-pattern.php

The following is an example of a simple site footer pattern:

<?php
/**
 * Title: Footer with text, social links.
 * Slug: theme-slug/footer-default
 * Categories: text, site-footer
 * Block Types: core/template-part/footer
 * Viewport Width: 1280
 */

?>

<!-- wp:group {"align":"full","layout":{"inherit":true}} -->
<div class="wp-block-group alignfull">
    <!-- wp:group {"align":"wide","layout":{"type":"flex","allowOrientation":false,"justifyContent":"space-between"}} -->
    <div class="wp-block-group alignwide">
        <!-- wp:paragraph -->
        <p>&copy; <?php echo esc_html( gmdate( 'Y' ) ); ?> Your Company LLC · <a href="#"><?php echo esc_html__( 'Contact Us', 'theme-slug' ); ?></a></p>
        <!-- /wp:paragraph -->

        <!-- wp:social-links {"className":"is-style-logos-only"} -->
        <ul class="wp-block-social-links has-icon-color is-style-logos-only">
            <!-- wp:social-link {"url":"https://wordpress.org","service":"wordpress"} /-->
        </ul>
        <!-- /wp:social-links -->
    </div>
    <!-- /wp:group -->
</div>
<!-- /wp:group -->

This pattern would be saved in the patterns/ directory in your theme, e.g. patterns/footer-default.php.

The title and slug properties in the pattern's header are required.

PHP registration of block patterns

You can use the register_block_pattern() function to register patterns with the init hook.

function theme_slug_block_pattern() {
  register_block_pattern( ... );
}

add_action( 'init', 'theme_slug_block_pattern' );

The register_block_pattern() function requires two properties in order to properly register a custom pattern:

The following is a minimal example of a pattern with two buttons.

register_block_pattern(
    'theme-slug/my-awesome-pattern',
    array(
        'title'       => __( 'Two buttons', 'theme-slug' ),
        'description' => _x( 'Two horizontal buttons, the left button is filled in, and the right button is outlined.', 'Block pattern description', 'theme-slug' ),
        'categories'  => array( 'theme-slug-custom-category' ),
        'content'     => "<!-- wp:buttons {"layout":{"type":"flex","justifyContent":"center"}} --><div class="wp-block-buttons"><!-- wp:button --><div class="wp-block-button"><a class="wp-block-button__link">' . __( 'Go shopping', 'theme-slug' ) . '</a></div><!-- /wp:button --></div><!-- /wp:buttons --></div></div>",
    )
);

You can create patterns of blocks within the block editor and then copy from the code editor, and paste them into the 'content' property. The 'content' property contains the markup that your pattern will output when inserted in the block editor.

The 'categories' property is optional and its value will assign your pattern to either a core or custom pattern category (See: Registering a custom pattern category below).

Important considerations when creating block patterns

Some key things to keep in mind when you're creating block patterns in your theme, which we'll cover:

Translations (i18n) in patterns

Be sure to include the ability to translate strings when you're creating your block patterns. For example:

<!-- wp:heading -->
<h2>' . esc_html__( 'My great heading text', 'theme-slug' ) . '</h2>
<!-- /wp:heading -->

Notice the esc_html__( 'My great heading text', 'theme-slug' ) in the above code sample. This allows builders to translate your strings into other languages.

Linking assets (images) - use get_theme_file_uri()

If you're including inline assets in your patterns then you can use the get_theme_file_uri() function to point to assets within your theme. Here is an example:

<!-- wp:cover {"url":"' .
esc_url( get_theme_file_uri( 'assets/images/snazzy-cover-image.png' ) ) . '",
"id":18,"contentPosition":"top center","align":"wide"} 
-->

Copy/pasting patterns from the block editor

A typical workflow for creating block patterns starts in the block editor. You add, group, alter all your blocks and then copy and paste the results into either the 'content' = => 'your content' if you're using register_block_pattern() function, or just raw if you're placing in a new PHP file within your patterns/ directory. You may consider escaping the string before pasting with a tool like: https://onlinestringtools.com/escape-string

Including custom CSS classes in your patterns

If you desire to add custom CSS classes to your pattern markup then you will have to use the className attribute. For example:

<!-- wp:group {"className":"call-to-action"} -->
<div class="wp-block-group call-to-action">
    <!-- wp:heading {"level":3} -->
    <h3><?php esc_html_e( 'My heading', 'theme-slug' ); ?></h3>
    <!-- /wp:heading --></div>
<!-- /wp:group -->

Note the use of "className":"call-to-action" and within the related, following <div class="call-to-action">. Both entries are necessary for this to work.

Using 3rd-party blocks

It is highly recommended that you conditionally load 3rd-party blocks within your patterns. Otherwise, the experience for rendering patterns can appear broken for end users. Here are two methods for conditionally loading blocks.

Conditionally load patterns when using register_block_pattern()

Oftentimes, well written plugins offer a parent class or function for you to check against when relying on its functionality. You can use either the function_exists or class_exists check within the init hook to check and register your pattern using register_block_pattern(), as so:

add_action( 'init', function() {
    if ( function_exists( 'some_plugin_func' ) ) {
        register_block_pattern();
    }
} );

Conditionally loading patterns when using patterns/ directory

Since patterns that are found in the patterns/ directory are auto-registered then we need to reverse the operation by deregistering it based on if the plugin is active.

For example, if we had patterns/footer-default.php in our theme:

<?php
/**
 * Title: Footer with text, social links.
 * Slug: theme-slug/footer-default
 * Categories: text, site-footer
 * Block Types: core/template-part/footer
 * Viewport Width: 1280
 */

?>

<!-- block markup here -->

If this pattern relied on a 3rd-party block then we would unregister it with unregister_block_pattern(), but we would have to hook it to our init hook, like so:

add_action( 'init', function() {
    if ( ! function_exists( 'some_plugin_func' ) ) {
        unregister_block_pattern( 'theme-slug/footer-default' );
    }
} );

Alternatively, if there were just a single 3rd-party block amongst many core blocks within a pattern that is loaded through the patterns/ directory. Then we could use PHP within the pattern to check:

<?php
/**
 * Title: Footer with text, social links.
 * Slug: theme-slug/footer-default
 * Categories: text, site-footer
 * Block Types: core/template-part/footer
 * Viewport Width: 1280
 */

?>

<!-- some WP core blocks here -->

<?php if ( function_exists( 'some_plugin_func' ) ) : ?>
    <!-- special 3rd-party block is here -->
<?php endif; ?>

<!-- some more WP core blocks here -->

Keep in mind that the pattern will look differently without the block when the 3rd-party block is unavailable and the overall layout may appear broken depending on your styling.

Registering a custom pattern category

Organizing your block patterns into appropriate categories within the block inserter will help your users quickly find what they need.

You can register a custom category using the register_block_pattern_category() function. Here is an example:

if ( function_exists( 'register_block_pattern_category' ) ) {
    register_block_pattern_category(
      'theme-slug-query',
      array( 'label' => __( 'Query', 'theme-slug' ) )
   );
}

This example registers a custom "Query" pattern category, which you can then reference and assign when you're registering your patterns:

register_block_pattern(
    'theme-slug/my-custom-query-pattern',
    array(
            'categories'  => array( 'theme-slug-query' ),
        ...
        )
);

If you do not assign a category for your block pattern then it will just be automatically assigned to Uncategorized in the block inserter.

Unregister a block pattern category

You can use the unregister_block_pattern_category() helper function to remove previously registered block categories. Typically, you'll want to call it within the init hook, like so:

function theme_slug_unregister_pattern_categories() {
    unregister_block_pattern_category( 'theme-slug-query' );
}

add_action( 'init', 'theme_slug_unregister_pattern_categories' );

The unregister_block_pattern_category() function accepts one argument: title, which is the name of the block pattern category to be unregistered.

Removing or hiding block patterns

By default, WordPress registers several block patterns and categories. However, theme developers can unregister some or all of these using the following methods:

Furthermore, you can disable the calling and displaying of patterns from the WordPress Block Pattern Directory within you theme by utilizing the should_load_remote_block_patterns filter.

add_filter( 'should_load_remote_block_patterns', '__return_false' );
colorful-tones commented 2 years ago

@carolinan @kafleg @TeBenachi - this new doc is ready for review. This is my first contribution. Thanks!

@zzap I've moved label to Content Review. Is this correct process?

For overall group, I added in the certain links to Learn videos, which I'm not sure is a standard, but I thought it would be helpful to allow different learning modalities. It could like be a maintenance burden. I'm curious on any guidance around this.

zzap commented 2 years ago

Yes, that's great @colorful-tones. As for Learn videos, I'd hold on with that. We do plan to link documentation to Learn resources but they are in the process of merging lessons and videos so the URLs might change very soon. This is why we are waiting with any sort of inner linking. But great thinking, nevertheless. It is what we plan eventually.

colorful-tones commented 2 years ago

As for Learn videos, I'd hold on with that. We do plan to link documentation to Learn resources but they are in the process of merging lessons and videos so the URLs might change very soon. This is why we are waiting with any sort of inner linking. But great thinking, nevertheless. It is what we plan eventually.

@zzap thanks for clarifying. I've removed the links to video resources for now. 👍

TeBenachi commented 2 years ago

@colorful-tones thank you so much. Great write-up! Looks great to me! It’s so clear and concise.

Just a couple of small minor points I noticed, if I may.

There are two methods currently for registering or including patterns in your theme and both approaches are compatible with classic and block themes:

currently may not be necessary for easier maintenance.

You can create patterns of blocks within the block editor and then copy from the code editor, and paste them into the 'content' property. The 'content' property contains the markup that your pattern will output when inserted in the block editor.

Adding “from the code editor” might help some developers clarify a little further.

I think the pattern topic can go under the Advanced Theme Topics or create a new top-level category.

Thanks

colorful-tones commented 2 years ago

@TeBenachi thanks for the feedback. I've integrated both of your suggestions.

I think the pattern topic can go under the Advanced Theme Topics or create a new top-level category.

I do not have the ability to add this new doc here. May I have access please? Or, are Themes Team reps only allowed to access and add?

richtabor commented 2 years ago

I'd consider moving the PHP registration as the secondary method for adding patterns, as using /patterns/ is simpler and less involved technically speaking. I would also add a bit of what use cases should lead to one method v. the other.

carolinan commented 2 years ago

@colorful-tones the themes team do not have user management permission for the docs in order to give you permission.

carolinan commented 2 years ago

I can see the list of users names that has access, but I do not see yours. I am also not sure which the correct user role would be.

colorful-tones commented 2 years ago

I'd consider moving the PHP registration as the secondary method for adding patterns, as using /patterns/ is simpler and less involved technically speaking.

Great point and I agree. I'll fix that up when I'm at my desk.

I would also add a bit of what use cases should lead to one method v. the other.

@richtabor I would have to lean on your experience and knowledge here. I'm not clear on the indicators of choosing one over the other and the value of adding that additional context?

colorful-tones commented 2 years ago

@colorful-tones the themes team do not have user management permission for the docs in order to give you permission.

@carolinan thanks for checking and clarifying. I'll reach out to the Docs team to see if they're able to give me access. 👍

richtabor commented 2 years ago

I'm not clear on the indicators of choosing one over the other and the value of adding that additional context?

I'd say that themes should use the /patterns method, with the caveat that those patterns will not be available to sites running < WordPress 6.0. But if you're building a block theme, you should use the /patterns method 100% of the time.

richtabor commented 2 years ago

Plugins that register patterns (though there aren't many) would need to use the PHP method.

colorful-tones commented 2 years ago

@richtabor I swapped the sequence for patterns/ and PHP registration sections. I also updated and added a small addition here:

Do you think that is adequate guidance?

colorful-tones commented 2 years ago

I've submitted the post for review: "Block Patterns" under the "Advanced Theme Topics" (parent) on the Developer site.

Once approved, it would appear here: https://developer.wordpress.org/themes/advanced-topics/block-patterns/

Heads up @carolinan @kafleg @TeBenachi - I'm assuming it is you folks that will review and publish it?

Thanks everyone for your guidance! 🥳

colorful-tones commented 2 years ago

Adding a note here to add in a new section to this doc that addresses how to conditionally load 3rd party blocks in patterns. As flagged by @carolinan in Slack.

colorful-tones commented 2 years ago

@carolinan @justintadlock - I added a new section: "Using 3rd-party blocks". Please review and let me know if there is any feedback. Thanks!

annezazu commented 2 years ago

@colorful-tones not sure if this is desired to be included but wanted to note some of the fun stuff from the curating the editor doc. Perhaps just linking off to this one is more than enough for now but it would be great to talk about locking, prioritizing patterns, etc.

colorful-tones commented 2 years ago

@colorful-tones not sure if this is desired to be included but wanted to note some of the fun stuff from the curating the editor doc. Perhaps just linking off to this one is more than enough for now but it would be great to talk about locking, prioritizing patterns, etc.

@annezazu I think that is suitable for this doc and would be happy to add. I mostly want to get this published for now and could even add something while publishing the final doc, but just getting something published is better than nothing. 😄

I'll leave this open for another day or two for feedback, but then will likely publish and iterate from there.

annezazu commented 2 years ago

I hear you there :D Sounds great.

colorful-tones commented 2 years ago

@zzap I've just clicked the 'Submit for Review' on this draft. Curious, what team might be reviewing? Should I ping somebody?

carolinan commented 2 years ago

Please no more pings 😄 once is enough unless you don't hear anything for several weeks.

carolinan commented 2 years ago

@colorful-tones Are you still editing? The post is still locked.

colorful-tones commented 2 years ago

@carolinan no, I am no longer editing.

carolinan commented 2 years ago

I have some suggestions.

In the first paragraph it says: "They offer a means to expedite and elevate the editorial experience by offering users a preset arrangement of blocks to start building right away."

As a non native English speaker, the use of expedite and elevate and editorial experience is just confusing to me. My actual reaction was "Elevate? you mean like elevating an element in the design by adding a box shadow?" Can this be simplified and shortened?


The first code example was missing a translation function for part of the text. I have added it.


Below the first code example it says: "The title and slug properties in the pattern's header are required." Can we explain what these are? Part of that information is included for the PHP function, but not for the patterns folder.

And for the PHP function, it says that title and content are required, but it does not mention the slug.


In the translation code example, the text is escaped but not echoed. How we can best explain the difference between the methods here? In the PHP file in the patterns folder, the text needs to be echoed, but if we are using the PHP function to register the pattern, the text can't be echoed because we are returning the whole content... This difference got me more than once when I switched to using the patterns folder ;)


Linking assets code example Nit picking: Can we explain that this is not a full code example for the cover block? Because if someone tries to copy paste that code it will not work. In the cover block, the image source is used twice. Would it be easier to use an complete image block?


At the bottom, we should include a changelog. The first item will only show when the page was published. I have added this.

carolinan commented 2 years ago

OK a few more things that came to mind after I posted the comment above:

  1. Should it not include instructions for how to display the pattern in a template in a full site editing theme, using the pattern block?
  2. Can we link to Converting customizer settings to block patterns?
carolinan commented 2 years ago

On this list item patterns/ folder (recommended for block themes) can we add that it only works from 6.0?

colorful-tones commented 2 years ago

@carolinan thanks for all this awesome feedback! I'm going to look to incorporate it directly in the Make draft post. I'll drop a note here once it is all updated. 👍

colorful-tones commented 2 years ago

In the first paragraph it says: "They offer a means to expedite and elevate the editorial experience by offering users a preset arrangement of blocks to start building right away."

As a non native English speaker, the use of expedite and elevate and editorial experience is just confusing to me. My actual reaction was "Elevate? you mean like elevating an element in the design by adding a box shadow?" Can this be simplified and shortened?

I've changed to read: "They offer a means to refine the editorial experience by offering users a preset arrangement of blocks to start building right away." Does this seem clearer?


The first code example was missing a translation function for part of the text. I have added it.

Thank you! 👍


Below the first code example it says: "The title and slug properties in the pattern's header are required." Can we explain what these are? Part of that information is included for the PHP function, but not for the patterns folder.

And for the PHP function, it says that title and content are required, but it does not mention the slug.

I believe I've addressed this. Please peek and let me know.


In the translation code example, the text is escaped but not echoed. How we can best explain the difference between the methods here? In the PHP file in the patterns folder, the text needs to be echoed, but if we are using the PHP function to register the pattern, the text can't be echoed because we are returning the whole content... This difference got me more than once when I switched to using the patterns folder ;)

I've added clarification around this as well. Let me know.


Linking assets code example Nit picking: Can we explain that this is not a full code example for the cover block? Because if someone tries to copy paste that code it will not work. In the cover block, the image source is used twice. Would it be easier to use an complete image block?

I've converted the example from a Cover block to an Image block. Let me know.

At the bottom, we should include a changelog. The first item will only show when the page was published. I have added this.

You rock! Thank you! ❤️


OK a few more things that came to mind after I posted the comment above:

  1. Should it not include instructions for how to display the pattern in a template in a full site editing theme, using the pattern block?
  2. Can we link to Converting customizer settings to block patterns?

I've addressed these as well.


On this list item patterns/ folder (recommended for block themes) can we add that it only works from 6.0?

I've added this with a link to the Make post announcing the addition of this feature.


Summary

@carolinan ☝️

Again, thanks for the review and feedback. Please let me know if you have anything further. I hope you enjoy your weekend and get some relaxing cross-stitching in. 🕺

carolinan commented 2 years ago

The page is published and added to the menu. Thank you!