diff --git a/.gitignore b/.gitignore index 72027a2..fc1cd44 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +# Cursor +.cursor diff --git a/reference/04-Patterns/block-bindings-api.md b/reference/04-Patterns/block-bindings-api.md index 890b9d2..beb0263 100644 --- a/reference/04-Patterns/block-bindings-api.md +++ b/reference/04-Patterns/block-bindings-api.md @@ -239,17 +239,14 @@ Below is the full code from our example Call to Action. Here we can see that our Call to Action will display a nice fallback should our post meta fields be missing. ![Call to action with default settings applied](../../static/img/block-pattern-call-to-action-example.jpg) -
*Call to action with default settings applied* If we have our all of our desired data, we will display our custom content. ![An example of our populated post meta fields](../../static/img/block-pattern-bindings-post-meta-example.jpg) -
*An example of our populated post meta fields* ![Our dynamic Call to Action populated with our post meta fields](../../static/img/block-pattern-bindings-finished-example.jpg) -
*Our dynamic Call to Action populated with our post meta fields* ## Further reading diff --git a/reference/04-Patterns/synced-pattern-overrides.md b/reference/04-Patterns/synced-pattern-overrides.md index cad9980..ee035d6 100644 --- a/reference/04-Patterns/synced-pattern-overrides.md +++ b/reference/04-Patterns/synced-pattern-overrides.md @@ -47,7 +47,6 @@ When an Override enabled block is selected, after editing a **Reset** control is When the pattern itself *or* any of its Override enabled innerblocks are selected, the usual block Inspector Control "Settings" panel is replaced by a "Content" panel where we can select the blocks that allow for editing. ![The updated block settings for an override enabled Synced Pattern](../../static/img/block-pattern-synced-override-enabled.jpg) -
*The updated block settings for an override enabled Synced Pattern* On the next page, we'll take a look at what's going on under the hood of a Synced Pattern and learn how we can apply our own logic to manage the content of our Patterns using the Block Bindings API. diff --git a/reference/04-Patterns/synced-patterns.md b/reference/04-Patterns/synced-patterns.md index 9671d31..b5c1a94 100644 --- a/reference/04-Patterns/synced-patterns.md +++ b/reference/04-Patterns/synced-patterns.md @@ -9,7 +9,6 @@ Block Patterns are an excellent way to quickly add common components across our This is where Synced Patterns shine. We can create a global component which can be edited once to then update across all instances where it is used. For example an info card, a grid of logos, or perhaps a simple call to action are all excellent candidates which may require such a feature. We will use the latter in our examples. ![Call to action example pattern](../../static/img/block-pattern-call-to-action-example.jpg) -
*A simple Call to Action block pattern* :::caution @@ -29,7 +28,6 @@ To mark a pattern as Synced, you would follow these steps: That's it! You can now find your component via the block inserter under the Patterns tab > My Patterns *(and any other categories you declared in step 3)*. ![The modal window for adding a new Pattern](../../static/img/block-pattern-add-new-modal.jpg) -
*The modal window for adding a new Pattern* ## Updating a Synced Pattern @@ -43,7 +41,6 @@ To edit your Synced Pattern, you would do the following: 5. You can return to the page you were editing using the **Back** button located near the title in the upper center of the screen. ![The block toolbar to edit your Synced Pattern](../../static/img/block-pattern-synced-edit-original.jpg) -
*The block toolbar to edit your Synced Pattern* ## Creating Synced Patterns for Theme Authors diff --git a/src/css/docs-content.css b/src/css/docs-content.css index d6cf2d6..73991e8 100644 --- a/src/css/docs-content.css +++ b/src/css/docs-content.css @@ -11,3 +11,8 @@ main > .container { margin-bottom: 4rem; } + +main img:has(+ em) { + display: block; + margin-block-end: 4px; +} diff --git a/static/img/training/10up-block-theme-initial-movies-screenshot.png b/static/img/training/10up-block-theme-initial-movies-screenshot.png new file mode 100644 index 0000000..90fd311 Binary files /dev/null and b/static/img/training/10up-block-theme-initial-movies-screenshot.png differ diff --git a/static/img/training/10up-block-theme-initial-people-screenshot.png b/static/img/training/10up-block-theme-initial-people-screenshot.png new file mode 100644 index 0000000..f2bce83 Binary files /dev/null and b/static/img/training/10up-block-theme-initial-people-screenshot.png differ diff --git a/static/img/training/10up-block-theme-initial-screenshot.png b/static/img/training/10up-block-theme-initial-screenshot.png new file mode 100644 index 0000000..fbd07cb Binary files /dev/null and b/static/img/training/10up-block-theme-initial-screenshot.png differ diff --git a/static/img/training/editor-post-title-wrapper-modified.png b/static/img/training/editor-post-title-wrapper-modified.png new file mode 100644 index 0000000..933c791 Binary files /dev/null and b/static/img/training/editor-post-title-wrapper-modified.png differ diff --git a/static/img/training/editor-post-title-wrapper-pink.png b/static/img/training/editor-post-title-wrapper-pink.png new file mode 100644 index 0000000..7713dd1 Binary files /dev/null and b/static/img/training/editor-post-title-wrapper-pink.png differ diff --git a/static/img/training/editor-post-title-wrapper-unmodified.png b/static/img/training/editor-post-title-wrapper-unmodified.png new file mode 100644 index 0000000..df2b636 Binary files /dev/null and b/static/img/training/editor-post-title-wrapper-unmodified.png differ diff --git a/static/img/training/site-editor-default-appearance.png b/static/img/training/site-editor-default-appearance.png new file mode 100644 index 0000000..9714e2d Binary files /dev/null and b/static/img/training/site-editor-default-appearance.png differ diff --git a/static/img/training/site-editor-footer-options-dropdown.png b/static/img/training/site-editor-footer-options-dropdown.png new file mode 100644 index 0000000..774fd7b Binary files /dev/null and b/static/img/training/site-editor-footer-options-dropdown.png differ diff --git a/static/img/training/site-editor-footer-reset.png b/static/img/training/site-editor-footer-reset.png new file mode 100644 index 0000000..4fecbb1 Binary files /dev/null and b/static/img/training/site-editor-footer-reset.png differ diff --git a/static/img/training/site-editor-navigation-completed-frontend-footer.png b/static/img/training/site-editor-navigation-completed-frontend-footer.png new file mode 100644 index 0000000..526770c Binary files /dev/null and b/static/img/training/site-editor-navigation-completed-frontend-footer.png differ diff --git a/static/img/training/site-editor-navigation-completed-frontend-header.png b/static/img/training/site-editor-navigation-completed-frontend-header.png new file mode 100644 index 0000000..440290b Binary files /dev/null and b/static/img/training/site-editor-navigation-completed-frontend-header.png differ diff --git a/static/img/training/site-editor-navigation-completed.png b/static/img/training/site-editor-navigation-completed.png new file mode 100644 index 0000000..702f788 Binary files /dev/null and b/static/img/training/site-editor-navigation-completed.png differ diff --git a/static/img/training/site-editor-patterns-display.png b/static/img/training/site-editor-patterns-display.png new file mode 100644 index 0000000..d9dea49 Binary files /dev/null and b/static/img/training/site-editor-patterns-display.png differ diff --git a/training/Block-Based-Themes/01-overview.md b/training/Block-Based-Themes/01-overview.md index 953265e..08ab65f 100644 --- a/training/Block-Based-Themes/01-overview.md +++ b/training/Block-Based-Themes/01-overview.md @@ -1,16 +1,21 @@ -# Lesson 1: Anatomy of a block based theme +--- +sidebar_label: 1. Anatomy of a Block Based Theme +sidebar_position: 1 +--- -Block Based Themes _(sometimes referred to as Full Site Editing or FSE for short)_ are a new way of building WordPress themes. Instead of using PHP templates, you build your theme using blocks. This means that everything in your theme is a block, from the header to the footer, and everything in between. +# 1. Anatomy of a Block Based Theme -In this lesson, we'll take a look at the anatomy of a block based theme, and how you can start building your own. +Block Based Themes (sometimes referred to as Full Site Editing or FSE for short) are a new way of building WordPress themes. Instead of using PHP templates, you build your theme using blocks. This means that everything in your theme is a block, from the header to the footer, and everything in between. + +In this lesson, we'll take a look at the anatomy of a block based theme and how you can start building your own. ## The Basics -Even though it might seem like a big change, building a block based theme is not that different from building a traditional theme. The main difference is that instead of using PHP templates, you HTML template files that contain block markup. These files are stored in a `templates` folder in your theme. +Even though it might seem like a big change, building a block based theme is not that different from building a traditional theme. The main difference is that instead of using PHP templates, you use HTML template files that contain block markup. These files are stored in a `templates` folder in your theme. -Everything else is pretty much the same. Any theme still requires a `style.css` file for the theme metadata, and a `functions.php` file for any custom functions or hooks, and so on. +Everything else is pretty much the same. Any theme still requires a `style.css` file for the theme metadata and a `functions.php` file for any custom functions or hooks. -You can think of block based themes as a superset on top of traditional themes. If you look at the template hierarchy in WordPress (e.g. `index.php`, `single.php`, `page.php`, etc.), the introduction of block based themes doesn't change that. Instead of `index.php`, you now have `templates/index.html`, and so on. The same rules apply, but instead of PHP files, you use HTML files. The cool part here is that technically speaking you can even still use PHP files. The template hierarchy will look for `.html` files first, and then fall back to `.php` files if it can't find an `.html` version. +You can think of block based themes as a superset on top of traditional themes. If you look at the template hierarchy in WordPress (`index.php`, `single.php`, `page.php`, etc.), the introduction of block based themes doesn't change that. Instead of `index.php`, you now have `templates/index.html`, and so on. The same rules apply, but instead of PHP files, you use HTML files. Technically you can even still use PHP files: the template hierarchy will look for `.html` files first, and then fall back to `.php` files if it can't find an `.html` version. ![Block Based Theme File Structure](../../static//img/fse-template-hierarchy.png) @@ -53,7 +58,7 @@ $footer_template = do_blocks( $footer_template->content );

- + @@ -93,7 +98,7 @@ You **cannot use arbitrary HTML** in a block template. **It needs to be valid bl If you need custom markup that isn't already available via any of the core blocks you can create it as a custom block. You can learn more about how to do that in the [Blocks section](../Blocks/01-overview.md). -Block based themes don't mean that we no longer write custom blocks. But rather that the custom blocks we do build are much more atomic rather that the big monolithic blocks that we might have built in the past. +Block based themes don't mean that we no longer write custom blocks, but rather that the custom blocks we do build are much more atomic rather than the big monolithic blocks we might have built in the past. ![Monolithic vs. Atomic blocks](../../static/img/atomic-blocks.png) @@ -105,29 +110,29 @@ No one should have to hand author the block markup in the `.html` files. The edi ## The Site Editor -We just mentioned the site editor, and it's a key part of building block based themes. The site editor is where you create and edit your block templates and template parts. It's a visual editor that lets you see how your changes will look on the frontend in real-time. +We just mentioned the Site Editor, and it's a key part of building block based themes. The Site Editor is where you create and edit your block templates and template parts. It's a visual editor that lets you see how your changes will look on the frontend in real time. -We should think of the site editor as a kind of visual IDE for developing WordPress themes. It is a development feature rather that something we would expect end users to use. +We should think of the Site Editor as a kind of visual IDE for developing WordPress themes. It is a development feature rather than something we would expect end users to use. ## Theme.json There is a myth out there that you don't wite any CSS in a block based theme and instead do everything in the `theme.json` file. **This is not true.** -Whilst you may find core themes such as twentytwentyfive to strive for _zero css_ that is not at all a requirement and I would even go so far as to say it is only really something you should consider if you are building a theme for the WordPress theme directory. +Whilst you may find core themes such as Twenty Twenty-Five striving for "zero CSS," that is not at all a requirement. It is really only something you should consider if you are building a theme for the WordPress theme directory. -For custom built themes for client projects it brings very little value and adds a lot of complexity in terms of fighting with WordPress cores stylesheet specificity. +For custom-built themes on client projects, it brings very little value and adds a lot of complexity in terms of fighting with WordPress core's stylesheet specificity. :::tip -A rule of thumb is that you should `theme.json` as the source of truth for all your design tokens and settings. But any actual styles should be written in CSS files. +A rule of thumb is that you should use `theme.json` as the source of truth for all your design tokens and settings. But any actual styles should be written in CSS files. ::: ## Styles -One of the nice benefits of block based themes where everything is made out of blocks is, that only the styles for the blocks that are actually used on the page are loaded. So you WordPress essentially does some code splitting for you. +One of the nice benefits of block based themes where everything is made out of blocks is that only the styles for the blocks actually used on the page are loaded. WordPress essentially does some code splitting for you. -Whats even more interesting is that on top of just code splitting, WordPress also does some light critical CSS extraction for you. +What's even more interesting is that on top of code splitting, WordPress also does some light critical CSS extraction for you. -When a page is loaded WordPress parses the blocks from top to bottom. And any time it encounters a block it will try to load all the styles associated with that block. As it does that it has a little buffer where instead of loading the individual css file, it will take the styles and actually inline them into the head of the document. WordPress will to that until that buffer is full and then it will start loading the css remaining CSS files separately. That way the blocks at the top of the page should have all their styles already present on the page when they are rendered. +When a page is loaded, WordPress parses the blocks from top to bottom. Any time it encounters a block, it tries to load all the styles associated with that block. It has a small buffer where instead of loading the individual CSS file, it inlines the styles directly into the `` of the document. WordPress does that until the buffer is full, and then it starts loading the remaining CSS files separately. That way the blocks at the top of the page should have all their styles already present when they are rendered. ![Diagram how WordPress parses blocks top to bottom](../../static/img/block-css-inlining.png) @@ -143,12 +148,11 @@ Our theme scaffold is already setup for this and has a special `assets/css/block ## Takeaways -- Block based themes are a new way of building WordPress themes that use blocks instead of PHP templates. -- Block templates are HTML files that contain block markup. They need to be stored in a `templates` folder in your theme. -- The site editor is where you create and edit your block templates and template parts. -- You should use the site editor to create your block templates, rather than hand-authoring the block markup in the `.html` files. +- Block based themes use blocks instead of PHP templates. Everything from the header to the footer is a block. +- Block templates are HTML files that contain block markup, stored in a `templates` folder in your theme. +- The Site Editor is where you create and edit your block templates and template parts. +- Author templates in the Site Editor, not by hand. No one should have to hand-author block markup in `.html` files. - You can still use PHP templates in a block based theme, but it's recommended to stick with `.html` files as much as possible. -- You should use the `theme.json` file as the source of truth for your design tokens and settings, but write your styles in CSS files. -- You should write your CSS for individual blocks in separate CSS files in the `assets/css/blocks` folder. -- WordPress does some code splitting and critical CSS extraction for you in block based themes. -- The [Create Block Theme plugin](https://wordpress.org/plugins/create-block-theme/) is a must-have for building block based themes. +- Use the `theme.json` file as the source of truth for your design tokens and settings, but write your actual styles in CSS files. +- Write CSS for individual blocks in separate files in the `assets/css/blocks` folder. WordPress code-splits these automatically. +- WordPress does code splitting and critical CSS extraction for you in block based themes. diff --git a/training/Block-Based-Themes/02-using-10up-block-theme.md b/training/Block-Based-Themes/02-using-10up-block-theme.md new file mode 100644 index 0000000..928600f --- /dev/null +++ b/training/Block-Based-Themes/02-using-10up-block-theme.md @@ -0,0 +1,143 @@ +--- +sidebar_label: "2. Orientation: The 10up Block Theme" +sidebar_position: 2 +--- + +# 2. Orientation: The 10up Block Theme + +If your environment is [set up as outlined in the overview](./index.md#getting-started), let's take a quick look at the 10up Block Theme scaffold and what it provides on top of a vanilla block theme. + +## Learning Outcomes + +1. Know what the 10up WP Scaffold adds beyond a standard block theme. +2. Be able to run the build pipeline and see the result. + +## What the scaffold provides + +We'll be using the [10up WP Scaffold](https://github.com/10up/wp-scaffold) as our starting point. Beyond a standard block theme, it includes: + +- **10up-toolkit**: a zero-config webpack-based build pipeline that handles CSS and JS compilation, block asset detection, and hot reloading. +- **Auto-enqueue block CSS**: drop a CSS file into `assets/css/blocks/{namespace}/{block-name}.css` and it loads only when the block is present on the page. +- **Auto-register custom blocks**: create a folder with a `block.json` in `blocks/` and it's automatically registered. +- **PostCSS with globals and mixins**: custom media queries and mixins are available across all style entry points without manual imports. +- **10up WP framework integration**: uses the [10up WP Framework](https://github.com/10up/wp-framework) pattern for organized, auto-discovered PHP modules. +- **Sensible CSS architecture**: organized into base, blocks, components, globals, mixins, templates, and utilities. +- **A must use plugin**: a place to register post types, taxonomies, or other non-theme specific features. + +We'll explore the CSS architecture, JS entry points, editor style scopes, and PHP module system in detail as we encounter them in later lessons. For now, the goal is to understand the high-level structure and prove the build pipeline works. + +## Theme structure + +```bash +cd themes/10up-block-theme +``` + +
+View tree + +```bash +10up-block-theme/ +├── composer.json +├── functions.php +├── package.json +├── style.css +├── theme.json +├── assets/ +│ ├── css/ +│ │ ├── base/ +│ │ ├── blocks/ +│ │ │ └── core/ +│ │ ├── components/ +│ │ ├── editor-canvas-style-overrides.css +│ │ ├── editor-frame-style-overrides.css +│ │ ├── frontend.css +│ │ ├── globals/ +│ │ ├── mixins/ +│ │ ├── templates/ +│ │ └── utilities/ +│ ├── js/ +│ │ ├── block-extensions.js +│ │ └── frontend.js +│ └── fonts/ +├── blocks/ +├── parts/ +│ ├── footer.html +│ ├── header.html +│ ├── site-footer-legal-navigation-area.html +│ └── site-header-navigation-area.html +├── patterns/ +│ └── card.php +├── src/ +│ ├── Assets.php +│ ├── Blocks.php +│ ├── TemplateTags.php +│ └── ThemeCore.php +├── styles/ +│ ├── surface-primary.json +│ ├── surface-secondary.json +│ └── surface-tertiary.json +└── templates/ + ├── 404.html + ├── index.html + ├── single.html + └── singular.html +``` + +
+ +The key directories to know right now: + +| Directory | Purpose | +|-----------|---------| +| `templates/` | HTML template files (block markup) following the WordPress template hierarchy | +| `parts/` | Reusable template parts (header, footer, navigation areas) | +| `patterns/` | PHP-based block patterns | +| `theme.json` | Design tokens, settings, and layout configuration | +| `assets/css/` | CSS source files, organized by purpose | +| `assets/js/` | JS entry points for frontend and editor | +| `blocks/` | Custom block source files (auto-registered) | +| `src/` | PHP modules (auto-discovered via the 10up WP Framework) | +| `styles/` | Style variation JSON files | + +We'll dig into each of these as we work with them in upcoming lessons. + +## Task: Verify the build pipeline + +Let's do one quick exercise to prove the build pipeline works. + +The 10up Block Theme contains an opinionated modification to the block editor Post Title text input wrapper. Typically this appears as part of the content area of the post, however this can often times feel undesirable when using Page Header or Hero type blocks that display the title as it does on the frontend. Our theme modification makes this appear as part of the standard editor UI, providing a bit more visual clarity. + +![Screenshot of the unmodified post title wrapper](../../static//img/training/editor-post-title-wrapper-unmodified.png) +*The unmodified Post Title wrapper* + +![Screenshot of the 10up Block Theme modified post title wrapper](../../static//img/training/editor-post-title-wrapper-modified.png) +*The 10up Block Theme modified Post Title wrapper* + +1. Open `assets/css/editor-canvas-style-overrides.css`. + +2. Find the `background-color` declaration and change it from `#fff` to `pink`: + +```css +.editor-styles-wrapper .editor-post-title { + background-color: pink; +} +``` + +3. Run the build: + +```bash +npm run build +``` + +4. Open any post in the editor. The title area should now have a pink background. + +![Screenshot of the 10up Block Theme modified post title wrapper with pink background](../../static//img/training/editor-post-title-wrapper-pink.png) + +5. Revert the change back to `#fff` and rebuild. + +That's it. The build pipeline compiles CSS and JS from `assets/` into `dist/`, and WordPress loads the output. We'll explore how that enqueuing works in [Lesson 5](./05-styles.md) when we dive into the CSS architecture. + +## Takeaways + +- The 10up scaffold provides a build pipeline, CSS architecture, PHP module system, and auto-registration for blocks and block CSS out of the box. +- We'll explore each of these systems in depth as we use them in later lessons. diff --git a/training/Block-Based-Themes/03-site-editor.md b/training/Block-Based-Themes/03-site-editor.md new file mode 100644 index 0000000..c09a9c6 --- /dev/null +++ b/training/Block-Based-Themes/03-site-editor.md @@ -0,0 +1,177 @@ +--- +sidebar_label: 3. The Site Editor +sidebar_position: 3 +--- + +# 3. The Site Editor + +This is your first truly hands-on lesson. You'll use the Site Editor to update the site's navigation and footer, then export the changes back to your theme files. + +## Learning Outcomes + +1. Know how to navigate the Site Editor UI: Templates, Parts, Styles, and Navigation panels. +2. Be able to edit a template part visually and export the result to the theme. +3. Understand the "Copy All Blocks" workflow for getting markup out of the editor and into `.html` files. +4. Know when to use a template vs. a template part vs. a pattern. + +## Context + +The `10up-block-theme` ships with a header, footer, and two navigation area parts. The header references a navigation area part, and the footer has a multi-column layout. We'll simplify both to match our movie theme. + +```bash +parts/ +├── footer.html +├── header.html +├── site-footer-legal-navigation-area.html +└── site-header-navigation-area.html +``` + +## Tasks + +### 1. Tour the Site Editor + +Open the Site Editor via **Appearance > Editor**. + +![Screenshot of the Site Editor](../../static//img/training/site-editor-default-appearance.png) + +Explore the main panels: + +- **Styles** - Global Styles panel for design tokens _(colors, typography, spacing)_ as well as managing the appearance of specific blocks throughout the whole of the site +- **Navigation** - manage navigation menus +- **Pages** - create or edit Pages +- **Templates** - lists all templates in your theme (`index.html`, `single.html`, `404.html`, etc.) +- **Patterns** - reusable sections and template parts + +:::info +Note that the Site Editor lists no patterns currently even though the theme contains a `card.php` file in the `/patterns` directory. This is due to `Inserter: false` property in the files metadata. Set it `true` for the card to see it display it the editor and revert back to `false` when done. + +![Screenshot of the Patterns tab with Card set true](../../static//img/training/site-editor-patterns-display.png) +::: + +### 2. Edit the header navigation + +1. Open the **Navigation** tab in the Site Editor sidebar. +2. Click the three dots next to **Navigation** and select **Edit**. +3. Open the Document Overview along the top left side and select the **Navigation** block. +4. You can delete the "Page List" block within and then click the plus sign in the content area to **Add block**. +5. From there choose **Add block** again and select **Custom Link**. +6. Type `/movies` and hit **Enter**. Use the **Edit link** button to change the text to Movies. +7. Follow the same process to create a `/people` custom link. +8. Hit Save. + +![Screenshot of the completed Navigation in the editor](../../static//img/training/site-editor-navigation-completed.png) + +If you view the frontend of your site, you should see the new items in both your header and footer. + +![Screenshot of the completed Navigation on the frontend - header](../../static//img/training/site-editor-navigation-completed-frontend-header.png) +*Frontend header navigation* + +![Screenshot of the completed Navigation on the frontend- footer](../../static//img/training/site-editor-navigation-completed-frontend-footer.png) +*Frontend footer navigation* + +### 3. Edit the footer + +1. Open **Patterns > Template Parts > Footer** in the Site Editor. +2. Replace the existing footer with a different layout. You could modify or delete the Columns block entirely, change the separator style to "Dots", add additional blocks, etc. +3. Have fun playing around, Save your changes, and view the frontend to see it applied. + +### 4. Export changes to theme files + +Once you're happy with your changes in the Site Editor, you need to get them back into your theme's `.html` files: + +From the template part in the Site Editor: + +1. Click the Options dropdown (three dots) in the upper right near the Save button. +2. Choose **Copy all blocks**. + 1. Alternatively, you can open the **Code Editor** view (three dots > Code editor, or `Ctrl/Cmd + Shift + Alt + M`) and select all of the block markup and copy it. This is a nice view to be aware of when you wish to make small manual block attribute adjustments. +3. Paste this into the corresponding file in your theme at `parts/footer.html`. +4. **Reset the template part** in the Site Editor sidebar under **Settings > Template Part > Actions (three dots) > Reset** + +:::tip +That last step is pretty important, this clears the database customization so the editor reads from the theme file again. + After pasting markup into a theme file, always reset the template part in the Site Editor. Without resetting, the Site Editor continues to use the database version (your customization), not the theme file. Resetting ensures the editor and the theme file are in sync. +::: + +:::info +[Create Bock Theme](https://wordpress.org/plugins/create-block-theme/) is a very handy plugin that can help to manage these steps as well. We went with the manual approach for this training but it is worth checking out. +::: + +![Screenshot of the Options dropdown with relevant items in bold](../../static//img/training/site-editor-footer-options-dropdown.png) +*The Options dropdown with relevant items in bold* + +![Screenshot of the reset option](../../static//img/training/site-editor-footer-reset.png) +*The template part Reset button* + +:::warning +To keep our training in sync with the finished theme, please use this command to copy the completed version into the 10up Block Theme. + +```bash +cp themes/fueled-movies/parts/footer.html themes/10up-block-theme/parts/footer.html +``` +::: + +## Templates, parts, and patterns: when to use what + +| Concept | Lives in | Shared? | Use when | +| ------- | -------- | ------- | -------- | +| **Template** | `templates/` | N/A | Defining a full page layout | +| **Template part** | `parts/` | Yes, across templates | Reusable sections (header, footer) | +| **Pattern** | `patterns/` | Depends on usage | Starter content or template composition | + +The key distinction: template parts always maintain a live link. When you edit a part, every template that references it gets the update. + +Patterns have two modes. When an editor **inserts** a pattern into a post, the markup is copied and detached. But when a template **references** a pattern via ``, the pattern re-executes on every page load. We'll use patterns this way in [Lesson 9](./09-archive-templates-and-cards.md). + +## Registering custom templates + +When you need templates for custom post types, add them to the `customTemplates` array in `theme.json`. This is how the editor discovers them in the template picker: + +```json title="theme.json (partial)" +{ + "customTemplates": [ + { + "name": "archive-tenup-movie", + "title": "Movie Archives", + "postTypes": [] + }, + { + "name": "single-tenup-movie", + "title": "Single Movie", + "postTypes": ["tenup-movie"] + } + ] +} +``` + +The `postTypes` array tells the editor which post types can use this template. Archive templates use an empty array because they match based on the template hierarchy (`archive-{post-type}.html`), not by assignment to individual posts. + +We'll add these custom templates in [Lesson 4](./04-theme-json.md) when we configure `theme.json`. + +## Files changed (fueled-movies delta) + +| File | Change type | What changes | +| ---- | ----------- | ------------ | +| `parts/header.html` | Modified | Formatting, compact single-line block markup (whitespace only) | +| `parts/footer.html` | Modified | Replaced multi-column layout with copyright line + legal nav | +| `parts/site-header-navigation-area.html` | Modified | Added `align:"wide"` and `justifyContent:"left"` to navigation layout | + +## Ship it checkpoint + +- Navigation includes Movies and People links that resolve to `/movies/` and `/people/` +- Footer is simplified to a single copyright line with legal navigation +- Changes exist in `parts/*.html` files (exported from Site Editor) + +TODO_SUGGEST_SCREENSHOT + +## Takeaways + +- The Site Editor is your visual IDE for building templates and parts. Author templates here, not by hand. +- After exporting changes to theme files, reset the template part in the Site Editor so it reads from the file again. +- Template parts maintain a live link across templates. Patterns can be either detached (inserted) or live (referenced via ``). +- Register custom templates in `theme.json` under `customTemplates` so the editor knows they exist. + +## Further reading + +- [Block Patterns](/reference/Patterns/overview) +- [Block Based Templates](/reference/Themes/block-based-templates) +- [Block Template Parts](/reference/Themes/block-template-parts) diff --git a/training/Block-Based-Themes/04-theme-json.md b/training/Block-Based-Themes/04-theme-json.md new file mode 100644 index 0000000..66f67e0 --- /dev/null +++ b/training/Block-Based-Themes/04-theme-json.md @@ -0,0 +1,270 @@ +--- +sidebar_label: "4. theme.json: Design Tokens and Settings" +sidebar_position: 4 +--- + +# 4. theme.json: Design Tokens and Settings + +There's a misconception that block themes put all their styles in `theme.json`. This is not the case, and fighting core stylesheet specificity is a losing battle. + +The rule of thumb: **`theme.json` is the source of truth for design tokens and settings. Actual styles belong in CSS files.** + +## Learning Outcomes + +1. Know what belongs in `theme.json` (tokens, settings, layout constraints) and what doesn't (actual CSS). +2. Understand the cascade: default → theme → user. +3. Be able to add a new color, spacing size, or typography preset and use it in a template. +4. Know how `theme.json` values become CSS custom properties. + +## The cascade + +WordPress resolves `theme.json` values in three layers: + +1. **Default** — WordPress core ships built-in presets (default palette, default spacing, etc.) +2. **Theme** — Your `theme.json` overrides or extends the defaults +3. **User** — Changes made in the Site Editor's Global Styles panel override the theme + +This means an editor can always override your theme's tokens via the Site Editor. That's by design — the theme establishes sensible defaults, and editors customize from there. + +## Comparing the two themes + +The `10up-block-theme` ships a minimal `theme.json`: spacing presets, layout widths, a system font stack, and some viewport-aware calculations. The color palette is intentionally empty. + +The `fueled-movies` theme builds on that foundation significantly. Let's walk through what it adds. + +### Color palette + +```json title="theme.json (fueled-movies, partial)" +{ + "settings": { + "color": { + "defaultPalette": false, + "palette": [ + { + "slug": "yellow", + "color": "var(--wp--custom--color--yellow--primary)", + "name": "Yellow" + }, + { + "slug": "text-primary", + "color": "var(--wp--custom--color--text--primary)", + "name": "Text Primary" + }, + { + "slug": "background-primary", + "color": "var(--wp--custom--color--background--primary)", + "name": "Background Primary" + } + ] + } + } +} +``` + +Notice the indirection: palette colors reference `--wp--custom--*` variables rather than hardcoded hex values. This keeps the actual values in one place — the `settings.custom` block — and lets the palette entries act as aliases. Change the custom property, and every palette usage updates automatically. + +Setting `defaultPalette: false` removes WordPress's built-in colors from the editor picker. This ensures editors can only use your intentional palette. + +### Custom properties (`settings.custom`) + +The `settings.custom` block is where you define arbitrary CSS custom properties. WordPress generates `--wp--custom--*` variables from the nested structure: + +```json title="theme.json (fueled-movies, partial)" +{ + "settings": { + "custom": { + "color": { + "yellow": { + "primary": "#F5C518", + "secondary": "#D8C882" + }, + "text": { + "primary": "#C3C3C3", + "secondary": "#a3a3a3" + }, + "background": { + "primary": "#0E0E0E", + "secondary": "#1A1A1A", + "nav": "#080808aa" + } + } + } + } +} +``` + +This generates CSS custom properties like: + +- `--wp--custom--color--yellow--primary` → `#F5C518` +- `--wp--custom--color--text--primary` → `#C3C3C3` +- `--wp--custom--color--background--primary` → `#0E0E0E` + +You can use these anywhere — in CSS files, in `theme.json` style declarations, or via the editor's color controls. + +### Custom aspect ratios + +```json title="theme.json (fueled-movies, partial)" +{ + "settings": { + "dimensions": { + "aspectRatios": [ + { + "name": "Movie Poster - 2:3", + "ratio": "2:3", + "slug": "movie-poster" + } + ] + } + } +} +``` + +This adds a "Movie Poster" option to the aspect ratio picker when configuring Image or Featured Image blocks. It sits alongside the built-in ratios (16:9, 4:3, etc.). + +### Element-level styles + +The `fueled-movies` theme defines default button and link styles at the element level: + +```json title="theme.json (fueled-movies, partial)" +{ + "styles": { + "elements": { + "button": { + "color": { + "background": "var(--wp--custom--color--action--primary, #f5c518)", + "text": "#121212" + }, + "border": { + "radius": "10px" + }, + "shadow": "0 -2px 2px 0 rgba(0, 0, 0, 0.25) inset", + ":hover": { + "color": { + "background": "#ffe99a" + } + } + }, + "link": { + "color": { + "text": "var(--wp--custom--color--action--secondary)" + } + } + } + } +} +``` + +These provide consistent defaults across all buttons and links in the theme. Individual blocks can still override them. + +Notice the link element intentionally has **no `:hover` `textDecoration: none`** override. In [Lesson 5](./05-styles.md) we'll use a CSS `text-decoration-color` transition (transparent to `currentcolor`) for a smooth underline reveal on clickable cards. Setting `textDecoration: none` globally in theme.json would fight that approach. + +:::caution +Avoid putting layout or visual styles in `theme.json` `styles` beyond element-level defaults. For anything more specific, use CSS. Core stylesheet specificity will fight you otherwise. +::: + +### Spacing units + +```json title="theme.json (fueled-movies, partial)" +{ + "settings": { + "spacing": { + "units": ["px", "em", "rem", "vh", "vw", "%"] + } + } +} +``` + +This controls which spacing units appear in the editor's dimension controls. The scaffold already includes fluid spacing presets using `clamp()` — these generate responsive values that scale between viewport widths. + +### Layout width + +Update `layout.wideSize` from the scaffold's default to `1219px`: + +```json title="theme.json (fueled-movies, partial)" +{ + "settings": { + "layout": { + "wideSize": "1219px" + } + } +} +``` + +### Custom templates + +Register the four CPT-specific templates so the editor discovers them in the template picker: + +```json title="theme.json (fueled-movies, partial)" +{ + "customTemplates": [ + { "name": "archive-tenup-movie", "title": "Movie Archives", "postTypes": [] }, + { "name": "single-tenup-movie", "title": "Single Movie", "postTypes": ["tenup-movie"] }, + { "name": "archive-tenup-person", "title": "Person Archives", "postTypes": [] }, + { "name": "single-tenup-person", "title": "Single Person", "postTypes": ["tenup-person"] } + ] +} +``` + +Archive templates use an empty `postTypes` array because they match based on the template hierarchy (`archive-{post-type}.html`), not by assignment to individual posts. + +## How tokens become CSS + +Every preset in `theme.json` generates a CSS custom property following a naming convention: + +| Setting | CSS variable pattern | Example | +|---------|---------------------|---------| +| `settings.color.palette` | `--wp--preset--color--{slug}` | `--wp--preset--color--yellow` | +| `settings.spacing.spacingSizes` | `--wp--preset--spacing--{slug}` | `--wp--preset--spacing--24` | +| `settings.typography.fontFamilies` | `--wp--preset--font-family--{slug}` | `--wp--preset--font-family--system-font` | +| `settings.custom.*` | `--wp--custom--{path}` | `--wp--custom--color--yellow--primary` | + +The key difference: `preset` variables come from defined presets (palette, spacing sizes, font families). `custom` variables come from the `settings.custom` block. Both are equally usable in CSS, but presets also power the editor's UI controls (color picker, spacing controls, etc.). + +## Tasks + +1. **Add the `settings.custom` block** with the semantic color tokens shown above (yellow, text, background, transparent backgrounds, action colors, spacing tokens). + +2. **Add the 8-color palette** referencing custom properties (set `defaultPalette: false`). + +3. **Add the custom aspect ratio** for Movie Poster 2:3. + +4. **Add `styles.elements.button` and `styles.elements.link`** defaults as shown above. + +5. **Add `spacing.units`** array and update `layout.wideSize` to `1219px`. + +6. **Add the `customTemplates` array** for the 4 CPT templates. + +7. **Verify.** No build needed: `theme.json` changes are read directly by WordPress. Refresh the editor and confirm colors appear in the picker and the site is dark. + +TODO_SUGGEST_SCREENSHOT + +8. **Test the cascade.** Override one of your theme's colors in the Site Editor's Global Styles panel. Refresh and verify the user override wins. Reset it and verify the theme default returns. + +## Takeaways + +- `theme.json` is for tokens and settings. CSS is for styles. +- Every preset generates a CSS custom property you can use anywhere. +- `settings.custom` creates `--wp--custom--*` variables for anything you need. +- The cascade is default → theme → user. User overrides in the Site Editor win. +- Setting `defaultPalette: false` (and similar `default*: false` flags) removes core presets so only your intentional choices appear. +- Palette colors can reference custom properties for a single source of truth. + +## Files changed (fueled-movies delta) + +| File | Change type | What changes | +| ---- | ----------- | ------------ | +| `theme.json` | Modified | Added: 8-color palette, `settings.custom` with semantic tokens, `dimensions.aspectRatios` (Movie Poster 2:3), `styles.elements.button` and `styles.elements.link`, `spacing.units`, `customTemplates` (4 CPT templates), `layout.wideSize` 1200px to 1219px | + +## Ship it checkpoint + +- Site is dark with yellow accents +- Color picker shows the custom palette (no default WordPress colors) +- "Movie Poster 2:3" appears in the aspect ratio picker +- Buttons are yellow with inset shadow + +TODO_SUGGEST_SCREENSHOT + +## Further reading + +- [Theme.json Reference](/reference/Themes/theme-json) +- [Styles Reference](/reference/Themes/styles) diff --git a/training/Block-Based-Themes/05-styles.md b/training/Block-Based-Themes/05-styles.md new file mode 100644 index 0000000..c8398de --- /dev/null +++ b/training/Block-Based-Themes/05-styles.md @@ -0,0 +1,312 @@ +--- +sidebar_label: "5. Styles: CSS Architecture and Style Variations" +sidebar_position: 5 +--- + +# 5. Styles: CSS Architecture and Style Variations + +This lesson combines two related topics: how the scaffold organizes and code-splits CSS, and how style variations give editors controlled design choices. + +## Learning Outcomes + +1. Understand the autoenqueue pipeline: `assets/css/blocks/{namespace}/{block-name}.css` loads only when the block is present. +2. Know the difference between block CSS, component CSS, and base CSS. +3. Know what style variations are (`styles/{block-type}/{slug}.json`) and how they differ from JS-registered block styles. +4. Be able to create a style variation and a code-split block stylesheet. + +## Part A: Copy CSS from the finished theme + +Most of the CSS in this lesson is straightforward styling. Rather than writing every file from scratch, copy the CSS files from the `fueled-movies` answer-key theme and review the file table below to understand what each file does. + +### The CSS architecture + +The scaffold organizes CSS into purpose-specific directories: + +| Directory | Loads | When to use | Example | +| --------- | ----- | ----------- | ------- | +| `blocks/core/` | Per-block (autoenqueue) | Styling a core block | `separator.css`, `post-terms.css` | +| `blocks/{namespace}/` | Per-block (autoenqueue) | Styling a third-party block | `jetpack/contact-form.css` | +| `components/` | Globally via `frontend.css` | Styles that span multiple blocks | `card.css`, `header.css` | +| `base/` | Globally via `frontend.css` | Foundational resets and layout | `reset.css`, `layout.css` | +| `utilities/` | Globally via `frontend.css` | Single-purpose utility classes | `visually-hidden.css` | +| `templates/` | Globally via `frontend.css` | Template-specific styles | `index.css` | +| `globals/` | Not output directly | PostCSS variables available everywhere | `media-queries.css` | +| `mixins/` | Not output directly | PostCSS mixins available everywhere | `visually-hidden.css` | + +**Block-scoped vs global**: if a style only matters when a specific block is on the page, put it in `blocks/`. If it affects multiple blocks or the overall page, it belongs in `components/` or `base/`. + +### How autoenqueue works + +1. You create a CSS file at `assets/css/blocks/core/separator.css` +2. `10up-toolkit` compiles it to `dist/blocks/autoenqueue/core/separator.css` +3. `src/Blocks.php` globs `dist/blocks/autoenqueue/` and registers each file with `wp_enqueue_block_style()` +4. WordPress only loads it on pages where the corresponding block is present + +The `path` parameter in `wp_enqueue_block_style()` tells WordPress the local filesystem path so it can inline the stylesheet directly in the HTML `` as critical CSS (for stylesheets under 20KB). + +### Editor and frontend CSS scopes + +The CSS files directly in `assets/css/` provide the main theme style entry and some editor adjustments: + +| File | Scope | Loaded via | Purpose | +| ---- | ----- | ---------- | ------- | +| `frontend.css` | Frontend (and editor canvas) | `wp_enqueue_scripts` + `add_editor_style()` | Main theme styles, imports base/components/utilities/templates | +| `editor-frame-style-overrides.css` | Editor frame only | `enqueue_block_editor_assets` | Styles for the editor chrome outside the editing canvas (toolbar, sidebar) | +| `editor-canvas-style-overrides.css` | Editor canvas only | `enqueue_block_assets` (admin only) | Styles inside the canvas iframe, e.g. making the post title look like part of the editor UI | + +:::tip +The editor has **two CSS scopes**. The "frame" is everything outside the editing area. The "canvas" is the iframe where blocks render. `frontend.css` is loaded in the canvas via `add_editor_style()` so blocks look the same in the editor as they do on the frontend. The frame and canvas override files are separate. +::: + +### Files to copy from fueled-movies + +Copy these files into your `10up-block-theme`: + +- `assets/css/base/html.css` (new) +- `assets/css/base/layout.css` (modified, adds `accent-color`, `@view-transition`) +- `assets/css/base/index.css` (modified, adds `@import url("html.css")`) +- `assets/css/components/header.css`, `card.css`, `button.css` (new) +- `assets/css/components/index.css` (modified, adds imports) +- `assets/css/mixins/is-clickable-card.css` (new) +- `assets/css/blocks/core/separator.css`, `post-featured-image.css`, `post-terms.css`, `group.css` (new) +- `assets/css/utilities/layout.css` (new) +- `assets/css/utilities/visually-hidden.css` (modified, adds `.is-hidden`) +- `assets/css/utilities/index.css` (modified, adds import) +- `assets/js/clickable-cards.js` (new) +- `assets/js/frontend.js` (modified, adds `import './clickable-cards'`) + +:::info +`is-style-single-movie-backdrop` styles in `post-featured-image.css` won't be visually testable until we build the single templates in [Lesson 10](./10-block-bindings.md). The `.has-separator` styles in `group.css` won't have a toggle until [Lesson 11](./11-block-extensions.md). Both are harmless CSS that we're placing now. +::: + +After copying, run `npm run build` and verify the site looks styled. + +### Key CSS patterns + +**Sticky header** (`assets/css/components/header.css`): + +```css +header:where(.wp-block-template-part) { + backdrop-filter: saturate(180%) blur(20px); + background-color: var(--wp--custom--color--background--nav); + inset-block-start: var(--wp-admin--admin-bar--height, 0); + isolation: isolate; + position: sticky; + z-index: 1000; + + & .wp-block-site-title { + font-family: "SF Pro Display", "SF Pro Icons", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 21px; + font-weight: 600; + letter-spacing: 0.011em; + line-height: 1.1428; + } +} +``` + +**Genre pill styling** (`assets/css/blocks/core/post-terms.css`): + +```css +.wp-block-post-terms { + display: flex; + flex-wrap: wrap; + gap: var(--wp--custom--spacing--12); + + & .wp-block-post-terms__separator { display: none; } + + & [rel="tag"] { + background: var(--wp--custom--color--background--light-transparent-10); + border-radius: 45px; + color: var(--wp--custom--color--text--primary); + padding: 8px 18px; + text-decoration: none; + transition: background-color 0.2s ease; + + &:hover { background: var(--wp--custom--color--background--light-transparent-20); } + } +} +``` + +**Has-separator dots** (`assets/css/blocks/core/group.css`): + +```css +.wp-block-group.has-separator { + gap: 0; + + & > * { + align-items: center; + display: flex; + } + + & > *:not(:first-child)::before { + background-color: currentcolor; + block-size: 4px; + border-radius: 999px; + content: ""; + display: inline-flex; + inline-size: 4px; + margin-inline: var(--wp--custom--spacing--8); + } +} +``` + +### Clickable cards + +The `assets/js/clickable-cards.js` file is an accessibility-focused pattern based on [Inclusive Components](https://inclusive-components.design/cards/#theredundantclickevent). Add the `is-clickable-card` class to the card's wrapping Group in the pattern, and the JS makes the entire card clickable by forwarding clicks to the primary link (the post title heading link). It handles text selection, scroll detection, and Ctrl/Cmd+click. Students just need to copy the files. + +The heading link provides good screen reader context since it contains the post title. The `is-clickable-card` class is added via the "Additional CSS class(es)" panel in the block editor. This is fine because the card pattern is code-only (`Inserter: false`), so editors never interact with it. **If this were an editor-facing block, a block extension with a toggle control would be better** since classes added via the Additional CSS panel can be accidentally deleted with no way for editors to know how to restore them. + +Card hover CSS (`assets/css/components/card.css`) uses the mixin from `assets/css/mixins/is-clickable-card.css` to apply hover feedback. The title starts with a transparent underline and transitions to `currentcolor` on card hover (smooth underline reveal). The secondary button gets its hover state. This is why `theme.json` doesn't set `textDecoration: none` on link hover, as that would fight the CSS transition. + +```css title="assets/css/components/card.css" +.is-clickable-card .wp-block-post-title { + text-decoration: underline; + text-decoration-color: transparent; + transition: text-decoration-color 0.2s ease; +} + +.is-clickable-card { + + @mixin is-clickable-card-hover { + + [data-is-clickable-card-primary] { + text-decoration-color: currentcolor; + } + + .wp-block-button [data-is-clickable-card-secondary] { + background-color: var(--wp--custom--color--background--light-transparent-20, rgba(163, 163, 163, 0.3)); + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.25) inset; + } + } +} +``` + +The `@mixin is-clickable-card-hover` is defined in `assets/css/mixins/is-clickable-card.css` and encapsulates the `:has()` hover selector, excluding secondary interactive elements from triggering the card hover: + +```css title="assets/css/mixins/is-clickable-card.css" +@define-mixin is-clickable-card-hover { + &:where(:hover:not(:has([data-is-clickable-card-secondary]:hover, [data-is-clickable-card-secondary]:focus))) { + @mixin-content; + } +} +``` + +## Part B: Style variations + +### What are style variations? + +Style variations are JSON files in the `styles/` directory that give editors selectable design options in the block inspector's Styles panel. They use the `theme.json` schema and can target the full range of design tokens: colors, spacing, borders, shadows, and nested elements. + +### Hands-on: create style variations + +The `10up-block-theme` ships three global "surface" variations for the Group block. We'll replace these with targeted per-block variations. + +1. **Delete** `styles/surface-primary.json`, `styles/surface-secondary.json`, and `styles/surface-tertiary.json`. + +2. **Create `styles/button/secondary.json`**: transparent background, primary text, inset shadow: + +```json title="styles/button/secondary.json" +{ + "$schema": "https://schemas.wp.org/wp/6.9/theme.json", + "version": 3, + "title": "Secondary", + "slug": "secondary", + "blockTypes": ["core/button"], + "styles": { + "elements": { + "button": { + "color": { + "background": "var(--wp--custom--color--background--light-transparent-10, rgba(163, 163, 163, 0.15))", + "text": "var(--wp--custom--color--text--primary)" + }, + "shadow": "0 1px 2px 0 rgba(255, 255, 255, 0.08) inset", + ":hover": { + "color": { + "background": "var(--wp--custom--color--background--light-transparent-20, rgba(163, 163, 163, 0.3))" + }, + "shadow": "0 2px 2px 0 rgba(0, 0, 0, 0.25) inset" + }, + ":focus": { + "color": { + "background": "var(--wp--custom--color--background--light-transparent-20, rgba(163, 163, 163, 0.3))" + }, + "shadow": "0 2px 2px 0 rgba(0, 0, 0, 0.25) inset" + } + } + } + } +} +``` + +Explain the JSON structure: `$schema`, `version`, `title`, `slug`, `blockTypes`, and `styles.elements.button`. + +3. **Create `styles/group/secondary.json`**: 10px radius, transparent background, 32px padding. + +4. **Rebuild and verify**: "Secondary" style appears for Button and Group blocks in the editor. + +TODO_SUGGEST_SCREENSHOT + +### Style variations vs JS block styles + +Both show up in the editor's Styles panel, but they work very differently: + +| Feature | Style variations (JSON) | Block styles (JS) | +| ------- | ---------------------- | ----------------- | +| Format | JSON file in `styles/` | `registerBlockStyle()` in JS | +| What it does | Applies `theme.json`-style design tokens | Adds a CSS class name | +| Can target nested elements | Yes (`elements.button`, `elements.link`) | No | +| Can set spacing, borders, shadows | Yes | No, only via the added class in CSS | +| Registration | Automatic from file system | Manual via JS | + +:::caution +Button style variations are a known pain point. The `core/button` block renders differently in the editor vs the frontend (the editor uses an `` element inside `.wp-block-button` while the frontend may use a ` + + + + +``` + +#### Directive reference + +| Directive | What it does | Example | +| --------- | ------------ | ------- | +| `data-wp-interactive` | Declares which store this block uses | `"tenup/rate-movie"` | +| `data-wp-context` | Sets initial reactive context for this block instance | `{ "rating": null }` | +| `data-wp-text` | Replaces text content with a state value | `state.buttonText` | +| `data-wp-bind--{attr}` | Binds an HTML attribute to state | `data-wp-bind--aria-expanded="state.isPopoverOpen"` | +| `data-wp-on--{event}` | Attaches an event handler to an action | `data-wp-on--click="actions.clearRating"` | +| `data-wp-class--{name}` | Toggles a CSS class based on state | `data-wp-class--is-open="state.isPopoverOpen"` | +| `data-wp-init` | Runs a callback when the element enters the DOM | `callbacks.initPopover` | + +#### The do_blocks() pattern + +The `markup.php` uses `do_blocks()` to render Button blocks from PHP. This ensures the buttons get proper block-style-variation CSS applied: + +```php title="blocks/rate-movie/markup.php (do_blocks pattern)" +$trigger_button = ' + +
+ +
+ +'; + +echo do_blocks( $trigger_button ); +``` + +By wrapping the HTML in block comments and running it through `do_blocks()`, WordPress processes it as if it were a real block, applying any style variation CSS that's been registered. This is how the "is-style-secondary" variation on the Clear button gets its styling even though the button is defined in PHP. + +### 4. Walk through the store + +```js title="blocks/rate-movie/view-module.js" +import { store, getContext, getElement } from '@wordpress/interactivity'; + +const { state } = store('tenup/rate-movie', { + state: { + isPopoverOpen: false, + + get hasRating() { + const context = getContext(); + return context.rating !== null && context.rating > 0; + }, + + get buttonText() { + if (state.isPopoverOpen) return 'Rate'; + const context = getContext(); + return context.rating > 0 ? `${context.rating}/10` : 'Rate'; + }, + + get sliderValue() { + const context = getContext(); + return context.rating !== null ? context.rating : 1; + }, + }, + + actions: { + selectRating(event) { + const context = getContext(); + const value = parseInt(event.target.value, 10); + context.rating = value >= 1 && value <= 10 ? value : null; + }, + clearRating() { + getContext().rating = null; + }, + }, + + callbacks: { + initPopover() { + const { ref } = getElement(); + if (!ref) return; + const root = ref.closest('.wp-block-tenup-rate-movie') ?? ref.parentElement; + const popover = ref; + const button = root?.querySelector('.wp-block-tenup-rate-movie__trigger'); + + if (!popover || !button) return; + + const updateState = () => { + state.isPopoverOpen = popover.matches(':popover-open'); + button.setAttribute('aria-expanded', state.isPopoverOpen ? 'true' : 'false'); + }; + + popover.addEventListener('toggle', updateState); + updateState(); + }, + }, +}); +``` + +Key concepts: + +- **`store()`**: creates a reactive store namespaced to `'tenup/rate-movie'`. The namespace must match the `data-wp-interactive` attribute in the markup. +- **`getContext()`**: returns the reactive context for the current block instance. Each rate-movie block on the page has its own `rating` value. +- **Computed state**: `get` properties like `buttonText` are derived from context. They recompute automatically when their dependencies change. +- **Actions**: functions called by `data-wp-on--*` directives. `selectRating` reads the slider value and updates context. +- **Callbacks**: functions called by `data-wp-init` (on mount) or `data-wp-watch` (on change). `initPopover` sets up the native popover toggle listener. + +### Interaction flow + +1. User clicks the "Rate" button, the native `popover` API opens the dialog +2. `initPopover` callback fires on the `toggle` event, sets `state.isPopoverOpen = true` +3. `data-wp-bind--aria-expanded` updates the button's ARIA attribute +4. User drags the range slider, `data-wp-on--input` fires `actions.selectRating` +5. `selectRating` parses the value and sets `context.rating` +6. `data-wp-text="state.buttonText"` reactively updates to show `"7/10"` +7. User clicks "Clear", `actions.clearRating` sets `context.rating = null` +8. Button text reverts to "Rate" + +### 5. Add to the single movie template + +Revisit `templates/single-tenup-movie.html` in the Site Editor. Add `` in the movie header area (near the title and metadata row). + +Export the updated markup back to the theme file. + +TODO_SUGGEST_SCREENSHOT + +### 6. Test accessibility + +The rate-movie block demonstrates several important accessibility patterns: + +- **`aria-haspopup`** on the trigger button tells assistive technology a dialog will appear +- **`aria-expanded`** toggles dynamically via `data-wp-bind--aria-expanded` +- **`aria-modal`** and **`role="dialog"`** on the popover identify it as a modal +- **`aria-labelledby`** connects the popover to its heading +- **Visually hidden label** on the range slider: "Rate this movie from 1 to 10" +- **Native `popover` API** provides built-in focus management and Escape key behavior + +Use VoiceOver (macOS) or a screen reader to verify: +- `aria-expanded` toggles on the trigger button +- The popover is announced as a dialog +- Keyboard navigation works (Tab, Escape) + +:::caution +The Interactivity API is not a replacement for React. It's designed for server-rendered blocks that need client-side behavior. Editor-side interactivity still lives in `edit.js` with React. +::: + +## Files changed (fueled-movies delta) + +| File | Change type | What changes | +| ---- | ----------- | ------------ | +| `package.json` | Modified | Added `@wordpress/interactivity` dependency; `useScriptModules: true` in toolkit config | +| `blocks/rate-movie/block.json` | **New** | Interactive block metadata with `viewScriptModule` | +| `blocks/rate-movie/markup.php` | **New** | Server-rendered HTML with `data-wp-*` directives, `do_blocks()` pattern for buttons | +| `blocks/rate-movie/view-module.js` | **New** | Interactivity API store with state, actions, callbacks | +| `blocks/rate-movie/index.js` | **New** | Minimal editor registration | +| `blocks/rate-movie/style.css` | **New** | Popover, trigger, slider, and rating display styles | +| `templates/single-tenup-movie.html` | **Revisited** | Added `` in movie header area | + +## Ship it checkpoint + +- The block uses a view module with store state/actions (no console errors) +- Accessibility: popover labeling, `aria-expanded`, keyboard navigation all work +- State rules enforced (null initial, range 1-10, clear resets to null) +- Rating displays on the button after selection ("7/10") + +TODO_SUGGEST_SCREENSHOT + +## Takeaways + +- The Interactivity API adds frontend behavior to server-rendered blocks declaratively. +- Directives (`data-wp-on--click`, `data-wp-bind--*`, `data-wp-text`) connect HTML to store state. +- Always server-render the initial HTML in `markup.php`. The API enhances it, not replaces it. +- Each block instance has its own context via `data-wp-context` and `getContext()`. +- Computed state (`get` properties) automatically updates when dependencies change. +- Accessibility is not optional: ARIA attributes, keyboard support, screen reader testing. +- Use `do_blocks()` when outputting block markup from PHP to ensure style variations are applied. + +## Further reading + +- [Interactivity API getting started](/guides/interactivity-api-getting-started) diff --git a/training/Block-Based-Themes/index.md b/training/Block-Based-Themes/index.md index b1f0c46..be05ad4 100644 --- a/training/Block-Based-Themes/index.md +++ b/training/Block-Based-Themes/index.md @@ -7,10 +7,132 @@ keywords: [gutenberg, wordpress block editor, training, course, fse, full site e # 10up Block Based Themes Training +## Overview + +This training course will walk you through the building of a block-based WordPress theme. By the end, you'll have transformed [the 10up Block Theme](https://github.com/10up/wp-scaffold/tree/trunk/themes/10up-block-theme) into a fully functional movie database theme, complete with custom post types, block bindings, editor controls, and interactive blocks. + +If you're new to blocks entirely, we recommend starting with our [Building Blocks](/training/Blocks/) training course first. + +## Prerequisites + +- [Node.js](https://nodejs.org/) (>=20.0.0) and npm (>=9.0.0) +- PHP (>=8.3) +- [Composer](https://getcomposer.org/) +- MySQL (8+) +- Git +- A code editor +- Basic familiarity with the command line + +## Getting started + +### 1. Create a LocalWP site + +You can certainly do this course using your development environment of choice, but we recommend [LocalWP](https://localwp.com/) and have outlined the steps necessary to get started with it below. + +
+
Create a site
+
Create a new site
+
What's your site's name?
+
Block Based Theme Training
Advanced options > Local site domain: block-based-theme-training.local
+
Choose your environment
+
Custom
PHP version: 8.3.x
Web server: nginx
Database: MYSQL 8+
+
Set up WordPress
+
Username: admin
Password: password
Email: example@example.com
Is this a WordPress Multisite? No
+
+ +### 2. Clone the repository + +https://github.com/10up/block-based-theme-training + +Open your terminal and navigate to `~/Local Sites/block-based-theme-training/app/public`, or from the LocalWP dashboard screen for the site select **Site shell**. + +```bash +rm -rf wp-content + +git clone git@github.com:10up/block-based-theme-training.git wp-content + +cd wp-content + +nvm use && npm install && npm run build && composer install + +cd mu-plugins/10up-plugin && composer install + +cd ../.. + +cd themes/10up-block-theme && composer install + +cd ../.. +``` + :::info -This training course is currently in progress. Check back soon for updates. +Going forward, please work from the `wp-content` folder when executing commands such as `npm run build`. The course will later provide additional instructions under the assumption your terminal is in that directory. ::: -## Overview +### 3. Configure wp-config.php + +Add the following constants to your `wp-config.php`: + +```php +define( 'WP_DEBUG', true ); +define( 'WP_DEBUG_LOG', true ); +define( 'WP_DEBUG_DISPLAY', false ); +define( 'SCRIPT_DEBUG', true ); +define( 'WP_DEVELOPMENT_MODE', 'all' ); +``` + +### 4. Activate the theme + +Log in to `wp-admin`, go to **Appearance > Themes**, and activate the **10up Block Theme**. + +At any time, you may also activate the **Fueled Movies** theme to see the finished product of this training course. + +## Content Import + +Once your site is set up, import the sample movie and person content. Open the **Site Shell** from the LocalWP dashboard for your site and run the following. The default import will add 30 movies and a approximately 90 persons. Alternatively, you can choose to import only your favorite films and their stars using the id found in the URL of an IMDB entry (e.g. - `tt0094721`). + +```bash +# Recommended default, imports 30 movies and 3 members of their cast. +wp fueled-movies import + +# Import only your favorite movies and their stars via the IMDB ID as found in their URL. +# e.g. https://www.imdb.com/title/tt0094721 +# These should be movie ID's (not TV shows) as a comma seperated list. +wp fueled-movies import --ids=tt0094721,tt0910970,tt0068646 + +# Preview without creating posts +wp fueled-movies import --dry-run + +# Override default star limit (default: 3 per movie) +wp fueled-movies import --ids=tt0094721 --star-limit=5 +``` + +## What you should see + +Visit the frontend at [block-based-theme-training.local](http://block-based-theme-training.local), you should see the following. + +![Screenshot of the 10up Block Theme added to a new LocalWP site](../../static//img/training/10up-block-theme-initial-screenshot.png) + +To ensure our content has imported, visit [block-based-theme-training.local/movies](http://block-based-theme-training.local/movies) and [block-based-theme-training.local/people](http://block-based-theme-training.local/people). + +![Screenshot of the Movies archive page](../../static//img/training/10up-block-theme-initial-movies-screenshot.png) +![Screenshot of the People archive page](../../static//img/training/10up-block-theme-initial-people-screenshot.png) + +## Lessons + +1. [Anatomy of a Block Based Theme](./01-overview.md) +2. [Orientation: The 10up Block Theme](./02-using-10up-block-theme.md) +3. [The Site Editor](./03-site-editor.md) +4. [theme.json: Design Tokens and Settings](./04-theme-json.md) +5. [Styles: CSS Architecture and Style Variations](./05-styles.md) +6. [Render Filters and Block Styles](./06-render-filters-and-block-styles.md) +7. [Understanding the Content Model](./07-content-model.md) +8. [Editor Controls for Post Meta](./08-editor-controls.md) +9. [Archive Templates and the Card Pattern](./09-archive-templates-and-cards.md) +10. [Block Bindings and Single Templates](./10-block-bindings.md) +11. [Block Extensions: Extending Core Blocks](./11-block-extensions.md) +12. [Custom Blocks: Description Lists and Movie Runtime](./12-custom-blocks.md) +13. [Interactivity API: Rate a Movie](./13-interactivity-api.md) + +## Support -This training course is designed to help you build and customize block-based themes. It assumes some basic knowledge of general WordPress development and an understanding of what blocks are. If you're new to blocks, we recommend starting with our [Building Blocks](/training/Blocks/) training course. +If you run into issues with this training project, feel free to reach out in Slack to [`#10up-gutenberg`](https://10up.slack.com/archives/C8Z3WMN1K). We also welcome bug reports, suggestions and contributions via the [Issues & Discussions tab on GitHub](https://github.com/10up/gutenberg-best-practices/issues). diff --git a/training/Blocks/01-overview.md b/training/Blocks/01-overview.md index 9001d3a..c2b974f 100644 --- a/training/Blocks/01-overview.md +++ b/training/Blocks/01-overview.md @@ -1,4 +1,4 @@ -# Lesson 1: Anatomy of a block +# 1. Anatomy of a Block Blocks are the foundation of how we build WordPress sites at 10up. As of WordPress 5.0, the Block Editor has become the default editor and replaces TinyMCE in that role. To deliver the best editorial experience to our clients, we will develop custom blocks, block patterns, make customizations to the core blocks and make sure our styles are loaded in both the frontend and the editor. diff --git a/training/Blocks/02-cta-lesson.md b/training/Blocks/02-cta-lesson.md index f44d9c9..a4e0457 100644 --- a/training/Blocks/02-cta-lesson.md +++ b/training/Blocks/02-cta-lesson.md @@ -1,4 +1,4 @@ -# Lesson 2: Creating a simple Block +# 2. Creating a Simple Block This lesson provides a partially completed block that needs to have some features added to it to match the existing `cta-complete` block. All of the setup and files have been pre-configured as the focus here is to work with a block on its own without adding confusion around build tools and file locations. diff --git a/training/Blocks/03-styles.md b/training/Blocks/03-styles.md index 0e3fcc4..7d0523c 100644 --- a/training/Blocks/03-styles.md +++ b/training/Blocks/03-styles.md @@ -1,4 +1,4 @@ -# Lesson 3: Block styles +# 3. Block Styles In many of our projects at 10up, we need to change the style of some of the core blocks that ship with the block editor. Typically, we want to remove block styles that aren't needed, or add ones that help us achieve elements of the project's design. Block Styles have a rather narrow use-case though as other options like [extending a block](/reference/Blocks/block-extensions) often time creates a better user experience. diff --git a/training/Blocks/04-patterns.md b/training/Blocks/04-patterns.md index 00129a5..012e546 100644 --- a/training/Blocks/04-patterns.md +++ b/training/Blocks/04-patterns.md @@ -1,4 +1,4 @@ -# Lesson 4: Block Patterns +# 4. Block Patterns Block Patterns are a great way to get up and running quickly when editing a page in WordPress. They allow you to insert entire predefined templates so that you don't have to start from scratch. As an editor you get a really nice visual preview of what the pattern looks like. diff --git a/training/Blocks/05-variations.md b/training/Blocks/05-variations.md index 031f7cd..164bf1c 100644 --- a/training/Blocks/05-variations.md +++ b/training/Blocks/05-variations.md @@ -1,4 +1,4 @@ -# Lesson 5: Block variations +# 5. Block Variations In the last lessons, we learned how to add styles to blocks, and when to utilize patterns. In this lesson, we're going to learn how to create block variations to help our editors get up and running more quickly. diff --git a/training/Blocks/06-inner-blocks.md b/training/Blocks/06-inner-blocks.md index fc44147..a7c212d 100644 --- a/training/Blocks/06-inner-blocks.md +++ b/training/Blocks/06-inner-blocks.md @@ -1,4 +1,4 @@ -# Lesson 6: Inner Blocks / Nested Blocks +# 6. Inner Blocks / Nested Blocks ## Learning Outcomes diff --git a/training/Blocks/07-rich-text-formats.md b/training/Blocks/07-rich-text-formats.md index a7a988a..d59e8ef 100644 --- a/training/Blocks/07-rich-text-formats.md +++ b/training/Blocks/07-rich-text-formats.md @@ -1,4 +1,4 @@ -# Lesson 7: Rich Text Formats +# 7. Rich Text Formats ## Learning Outcomes diff --git a/training/Blocks/08-slot-fill.md b/training/Blocks/08-slot-fill.md index 82f6699..ea6d2c2 100644 --- a/training/Blocks/08-slot-fill.md +++ b/training/Blocks/08-slot-fill.md @@ -1,4 +1,4 @@ -# Lesson 8: SlotFill +# 8. SlotFill With the introduction of Gutenberg in WordPress 5.0, developers lost the ability to extend the post editor using PHP hooks. Instead, we now have the SlotFill system. At a high level, the SlotFill system allows developers to inject UI elements into Gutenberg into a predefined set of locations. For the full list of available locations please refer to the [SlotFills Reference docs on developer.wordpress.org](https://developer.wordpress.org/block-editor/reference-guides/slotfills/) @@ -36,7 +36,7 @@ Here's the result in the editor: ### 1. Registering a plugin -In order to add a Fill to any of the available Slots, we first need to register a plugin using `registerPlugin` from the `@wordpress/plugins` package. Import the function and pass the appropriate parameters as defined in the [official docs](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-plugins/#registerplugin). In the starter files, the SlotFill we're working with has already been imported do you only need to add the `CustomDocumentSettingsPanel` to the `render` property of `registerPlugin`. +In order to add a Fill to any of the available Slots, we first need to register a plugin using `registerPlugin` from the `@wordpress/plugins` package. Import the function and pass the appropriate parameters as defined in the [official docs](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-plugins/#registerplugin). In the starter files, the SlotFill we're working with has already been imported so you only need to add the `CustomDocumentSettingsPanel` to the `render` property of `registerPlugin`. ### 2. Prepare the post meta for the REST API @@ -44,7 +44,7 @@ In order to access or save post meta in Gutenberg, the meta needs to be exposed ### 3. Add the ToggleControl component -Import the `ToggleControl` component and add it inside the `PluginDocumentSettingPanel` component. Anything inside that component will be displayed in the sidebar. Using the [official [docs](https://developer.wordpress.org/block-editor/reference-guides/components/toggle-control/) as a reference, set up the `ToggleControl` so that the value of the meta is set to `dark-mode` when checked and `light-mode` when unchecked and save the meta using the `editPost` function. +Import the `ToggleControl` component and add it inside the `PluginDocumentSettingPanel` component. Anything inside that component will be displayed in the sidebar. Using the [official docs](https://developer.wordpress.org/block-editor/reference-guides/components/toggle-control/) as a reference, set up the `ToggleControl` so that the value of the meta is set to `dark-mode` when checked and `light-mode` when unchecked and save the meta using the `editPost` function. :::warning **You will be updating all of the meta for the post, so be sure to combine the existing and new meta values when saving. If you get stuck, refer to the complete example.** diff --git a/training/Blocks/09-build-your-own.md b/training/Blocks/09-build-your-own.md index 971d067..b8b552f 100644 --- a/training/Blocks/09-build-your-own.md +++ b/training/Blocks/09-build-your-own.md @@ -1,4 +1,4 @@ -# Lesson 9: Build Your Own +# 9. Build Your Own After going through the lessons, it's time to put all your newly learned skills together and build a block by yourself. Spend no more than ~4 hours on this block. diff --git a/training/Blocks/10-Using the Block Scaffold command.md b/training/Blocks/10-Using the Block Scaffold command.md index 70e7bac..f0c3de8 100644 --- a/training/Blocks/10-Using the Block Scaffold command.md +++ b/training/Blocks/10-Using the Block Scaffold command.md @@ -1,4 +1,4 @@ -# Lesson 10: Scaffolding with the Block Scaffold Command +# 10. Scaffolding with the Block Scaffold Command ## Overview diff --git a/training/Blocks/index.md b/training/Blocks/index.md index b121144..e5c6704 100644 --- a/training/Blocks/index.md +++ b/training/Blocks/index.md @@ -103,15 +103,16 @@ If there is a `markup.php` file located inside the same directory as the `block. ## Lessons -* [Lesson 1: Anatomy of a block](./01-overview.md) -* [Lesson 2: A Simple CTA block](./02-cta-lesson.md) -* [Lesson 3: Block Styles](./03-styles.md) -* [Lesson 4: Block Patterns](./04-patterns.md) -* [Lesson 5: Block Variations](./05-variations.md) -* [Lesson 6: Inner Blocks / Block Nesting](./06-inner-blocks.md) -* [Lesson 7: Rich Text Formats](./07-rich-text-formats.md) -* [Lesson 8: Slot Fill](./08-slot-fill.md) -* [Lesson 9: Build your own](./09-build-your-own.md) +1. [Anatomy of a Block](./01-overview.md) +2. [A Simple CTA Block](./02-cta-lesson.md) +3. [Block Styles](./03-styles.md) +4. [Block Patterns](./04-patterns.md) +5. [Block Variations](./05-variations.md) +6. [Inner Blocks / Block Nesting](./06-inner-blocks.md) +7. [Rich Text Formats](./07-rich-text-formats.md) +8. [Slot Fill](./08-slot-fill.md) +9. [Build Your own](./09-build-your-own.md) +10. [Scaffolding with the Block Scaffold Command](./10-Using%20the%20Block%20Scaffold%20command.md) ## Support