├── .github ├── PULL_REQUEST_TEMPLATE.md ├── labeler.yml └── workflows │ ├── check-build.yml │ ├── deploy.yml │ └── label.yml ├── .gitignore ├── .gitlab-ci.yml ├── .markdownlint.json ├── .nvmrc ├── babel.config.js ├── deploy-scripts ├── build.sh └── deploy-gutenberg.sh ├── docusaurus.config.js ├── guides ├── api-version-2.md ├── choose-your-adventure.md ├── data-api.md ├── extend-a-core-block.md ├── external-libraries.md ├── handeling-block-spacing.md ├── html-tag-processor.md ├── including-frontend-javascript-with-a-block.md ├── index.md ├── interactivity-api-getting-started.md ├── modifying-the-markup-of-a-core-block.md ├── pitfals-style-api.md ├── tools-panel.md └── using-wordpress-packages-on-the-frontend.md ├── package-lock.json ├── package.json ├── readme.md ├── reference ├── 01-Fundamentals │ ├── a-block.md │ └── the-editor.md ├── 02-Themes │ ├── block-based-templates.md │ ├── block-template-parts.md │ ├── fonts.md │ ├── navigation.md │ ├── styles.md │ └── theme-json.md ├── 03-Blocks │ ├── block-extensions.md │ ├── block-locking.md │ ├── block-styles.md │ ├── block-supports.md │ ├── block-transforms.md │ ├── block-updates.md │ ├── block-variations.md │ ├── custom-blocks.md │ ├── inner-blocks.md │ └── unregister-block.md ├── 04-Patterns │ ├── block-bindings-api.md │ ├── overview.md │ ├── synced-pattern-overrides.md │ └── synced-patterns.md ├── 05-custom-post-types.md └── index.md ├── sidebars.js ├── src ├── css │ ├── custom.css │ ├── docs-content.css │ ├── global-footer.css │ ├── global-header.css │ ├── global-sidebar.css │ ├── global-site.css │ ├── homepage-components.css │ ├── homepage-search.css │ ├── toc.css │ └── variables.css ├── pages │ ├── index.js │ └── index.module.css └── theme │ └── Footer │ └── index.js ├── static ├── .nojekyll ├── Virgil.woff2 ├── google761431273f6cb4b8.html ├── img │ ├── 10up-logo-full.svg │ ├── applications-cta-blank.png │ ├── applications-cta-inserter.png │ ├── atomic-blocks.png │ ├── background-pattern-questionmarks.png │ ├── block-anatomy-anotated.png │ ├── block-css-inlining.png │ ├── block-deselected-state.jpg │ ├── block-editor-sidebar-panels.png │ ├── block-editor.png │ ├── block-extenstions-after.png │ ├── block-extenstions-before.png │ ├── block-initial-setup-state.png │ ├── block-lock-activated.png │ ├── block-locking-ui.gif │ ├── block-migration.jpg │ ├── block-pattern-add-new-modal.jpg │ ├── block-pattern-bindings-finished-example.jpg │ ├── block-pattern-bindings-post-meta-example.jpg │ ├── block-pattern-call-to-action-example.jpg │ ├── block-pattern-save.png │ ├── block-pattern-synced-edit-original.jpg │ ├── block-pattern-synced-override-enabled.jpg │ ├── block-patterns-structure-diagram.png │ ├── block-reference-sketch.png │ ├── block-selected-state.jpg │ ├── block-settings-sidebar.png │ ├── block-template-part-editor.png │ ├── block-template-parts.png │ ├── block-toolbar-groups.png │ ├── block-toolbar.png │ ├── block-transforms.png │ ├── block-variations-example.png │ ├── columns-block-variations-picker.png │ ├── content-only-pattern-modify.png │ ├── content-only-pattern.gif │ ├── content-only-pattern.png │ ├── contrib-block.png │ ├── contrib-block@2x.png │ ├── contrib-docs.png │ ├── contrib-docs@2x.png │ ├── contrib-scaffold.png │ ├── contrib-scaffold@2x.png │ ├── core-embed-variations-inserter.png │ ├── core-image-rounded.png │ ├── core-image-slightly-rounded.png │ ├── core-image-square.png │ ├── core-image-styles.png │ ├── core-image-variations.jpg │ ├── core-rich-text-formats-screenshot.jpg │ ├── css-file-names.png │ ├── cta-block-style.png │ ├── cta-block-thick-border.png │ ├── cta-complete-frontend.png │ ├── cta-complete-with-control.png │ ├── cta-complete.png │ ├── data-api-core-stores.png │ ├── data-api-loading-error.png │ ├── data-api-wordpress-db.png │ ├── deep-dive.png │ ├── design-styleguide-spacing-scale.png │ ├── dimensions-panel.png │ ├── docusaurus.png │ ├── dynamic-map-editor-view.png │ ├── dynamic-map-frontend.png │ ├── embed-block-variations-overview.png │ ├── favicon.ico │ ├── final-g-wapuu-black.svg │ ├── final-g-wapuu-white.svg │ ├── flexibility-scale.png │ ├── fse-template-hierarchy.png │ ├── global-styles-input-output.png │ ├── got-questions.png │ ├── guides-sketch.png │ ├── gutenberg-g-white.svg │ ├── gutenberg-interface-sketch.png │ ├── gutenberg-interface-sketch.svg │ ├── gutenberg-interface.jpg │ ├── gutenberg-sidebar.png │ ├── gutenberg-toolbar.png │ ├── image-block-block-styles.png │ ├── image-block-styles.png │ ├── inner-blocks-core-columns-screenshot.jpg │ ├── inner-blocks-core-group-screenshot.jpg │ ├── inner-blocks-one-mockup.png │ ├── inner-blocks-one-scribble.png │ ├── inner-blocks-two-mockup.png │ ├── inner-blocks-two-scribble.png │ ├── logo.svg │ ├── pattern-modal.png │ ├── pullquote-core-block-style-removed.png │ ├── pullquote-core-block-style.png │ ├── redux-api-design-simplified.png │ ├── redux-api-design.png │ ├── reference-icon.png │ ├── reference-sketch.png │ ├── sample-design-boxes.png │ ├── slotfill-light-dark-mode.png │ ├── spacing-presets-in-use.gif │ ├── supports-align.png │ ├── supports-anchor.png │ ├── supports-classname.png │ ├── supports-color.png │ ├── supports-default-style-picker.png │ ├── supports-dimension.png │ ├── supports-edit-html.png │ ├── supports-typography.png │ ├── text-format-design.png │ ├── training-sketch.png │ ├── tutorial │ │ ├── docsVersionDropdown.png │ │ └── localeDropdown.png │ ├── undraw_docusaurus_mountain.svg │ ├── undraw_docusaurus_react.svg │ ├── undraw_docusaurus_tree.svg │ ├── variations-block-cta-1.png │ ├── variations-block-cta-2.png │ ├── variations-block-next-steps-1.png │ ├── variations-block-next-steps-2.png │ └── what-to-build.png └── robots.txt └── training ├── Block-Based-Themes ├── 01-overview.md └── index.md ├── Blocks ├── 01-overview.md ├── 02-cta-lesson.md ├── 03-styles.md ├── 04-patterns.md ├── 05-variations.md ├── 06-inner-blocks.md ├── 07-rich-text-formats.md ├── 08-slot-fill.md ├── 09-build-your-own.md ├── 10-Using the Block Scaffold command.md └── index.md └── index.md /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description of the Change 4 | 5 | 6 | Closing # 7 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | Guide: 2 | - guides/* 3 | 4 | Reference: 5 | - reference/* 6 | 7 | Training: 8 | - training/* 9 | -------------------------------------------------------------------------------- /.github/workflows/check-build.yml: -------------------------------------------------------------------------------- 1 | name: Ensure the build is passing 2 | 3 | on: 4 | pull_request 5 | 6 | jobs: 7 | test: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | - name: Install NPM Dependencies 14 | run: npm ci 15 | - name: Run the build to ensure it doesn't fail 16 | run: npm run build 17 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Production Website 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build-and-deploy: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | with: 15 | fetch-depth: 0 16 | 17 | - name: Install NPM Dependencies 18 | run: npm ci 19 | 20 | - name: Build Assets 21 | run: npm run build 22 | 23 | - name: Install SSH Key 24 | uses: shimataro/ssh-key-action@v2 25 | with: 26 | key: ${{ secrets.SSH_PRIVATE_KEY }} 27 | known_hosts: unnecessary 28 | config: | 29 | Host * 30 | StrictHostKeyChecking no 31 | 32 | - name: Deploy Website 33 | run: rsync -vrxc --delete build/ gitlab@147.182.201.45:/var/www/gutenberg.10up.com/ 34 | -------------------------------------------------------------------------------- /.github/workflows/label.yml: -------------------------------------------------------------------------------- 1 | # This workflow will triage pull requests and apply a label based on the 2 | # paths that are modified in the pull request. 3 | # 4 | # To use this workflow, you will need to set up a .github/labeler.yml 5 | # file with configuration. For more information, see: 6 | # https://github.com/actions/labeler 7 | 8 | name: Labeler 9 | on: [pull_request] 10 | 11 | jobs: 12 | label: 13 | 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: read 17 | pull-requests: write 18 | 19 | steps: 20 | - uses: actions/labeler@v4 21 | with: 22 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | /dist 7 | 8 | # Generated files 9 | .docusaurus 10 | .cache-loader 11 | 12 | # Misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: docker.10up.com/10up-build/deploy-container:latest 2 | 3 | stages: 4 | - build 5 | - deploy 6 | 7 | cache: 8 | paths: 9 | - node_modules_cache 10 | 11 | build: 12 | stage: build 13 | script: 14 | - bash ./deploy-scripts/build.sh 15 | when: always 16 | 17 | deploy_gutenberg: 18 | stage: deploy 19 | environment: 20 | name: gutenberg 21 | url: https://gutenberg.10up.com 22 | script: 23 | - bash ./deploy-scripts/build.sh 24 | - bash ./deploy-scripts/deploy-gutenberg.sh 25 | only: 26 | - main 27 | allow_failure: false 28 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "MD033": false, 3 | "MD013": false, 4 | "MD010": false, 5 | "MD029": false 6 | } -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /deploy-scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # catch errors 4 | set -euo pipefail 5 | 6 | nvm use 18 7 | npm install 8 | npm run build 9 | -------------------------------------------------------------------------------- /deploy-scripts/deploy-gutenberg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # catch errors 4 | set -euo pipefail 5 | # output to logs 6 | set -x 7 | 8 | rsync -vrxc --delete build/ gitlab@147.182.201.45:/var/www/gutenberg.10up.com/ 9 | -------------------------------------------------------------------------------- /docusaurus.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Note: type annotations allow type checking and IDEs autocompletion 3 | import { themes } from 'prism-react-renderer'; 4 | 5 | const lightCodeTheme = themes.github; 6 | const darkCodeTheme = themes.dracula; 7 | 8 | /** @type {import('@docusaurus/types').Config} */ 9 | const config = { 10 | title: '10up - WP Block Editor Best Practices', 11 | tagline: 'The central hub for everything Block Editor related at 10up', 12 | url: 'https://gutenberg.10up.com', 13 | baseUrl: '/', 14 | onBrokenLinks: 'throw', 15 | onBrokenMarkdownLinks: 'warn', 16 | favicon: 'img/favicon.ico', 17 | organizationName: '10up', // Usually your GitHub org/user name. 18 | projectName: 'gutenberg-best-practices', // Usually your repo name. 19 | 20 | presets: [ 21 | [ 22 | 'classic', 23 | /** @type {import('@docusaurus/preset-classic').Options} */ 24 | ({ 25 | docs: false, 26 | blog: false, 27 | gtag: { 28 | trackingID: 'G-WF1Z7JSCXS', 29 | anonymizeIP: true, 30 | }, 31 | theme: { 32 | customCss: require.resolve('./src/css/custom.css'), 33 | }, 34 | }), 35 | ], 36 | ], 37 | 38 | plugins: [ 39 | [ 40 | '@docusaurus/plugin-content-docs', 41 | { 42 | id: 'guides', 43 | path: 'guides', 44 | routeBasePath: 'guides', 45 | sidebarPath: require.resolve('./sidebars.js'), 46 | showLastUpdateTime: true, 47 | showLastUpdateAuthor: true, 48 | editUrl: 'https://github.com/10up/gutenberg-best-practices/tree/main/', 49 | sidebarCollapsed: false, 50 | }, 51 | ], 52 | [ 53 | '@docusaurus/plugin-content-docs', 54 | { 55 | id: 'training', 56 | path: 'training', 57 | routeBasePath: 'training', 58 | sidebarPath: require.resolve('./sidebars.js'), 59 | showLastUpdateTime: true, 60 | showLastUpdateAuthor: true, 61 | editUrl: 'https://github.com/10up/gutenberg-best-practices/tree/main/', 62 | sidebarCollapsed: false, 63 | }, 64 | ], 65 | [ 66 | '@docusaurus/plugin-content-docs', 67 | { 68 | path: 'reference', 69 | routeBasePath: 'reference', 70 | sidebarPath: require.resolve('./sidebars.js'), 71 | showLastUpdateTime: true, 72 | showLastUpdateAuthor: true, 73 | editUrl: 'https://github.com/10up/gutenberg-best-practices/tree/main/', 74 | sidebarCollapsed: false, 75 | }, 76 | ], 77 | [ 78 | require.resolve("@easyops-cn/docusaurus-search-local"), 79 | { 80 | 81 | indexDocs: true, 82 | docsRouteBasePath: ['reference', 'guides', 'training'], 83 | docsDir: ['reference', 'guides', 'training'], 84 | hashed: true, 85 | 86 | }, 87 | ], 88 | [ 89 | require.resolve('@docusaurus/plugin-client-redirects'), 90 | { 91 | redirects: [ 92 | { 93 | to: '/training/Blocks/overview/', 94 | from: '/training/overview/', 95 | }, 96 | { 97 | to: '/training/Blocks/cta-lesson/', 98 | from: '/training/cta-lesson/', 99 | }, 100 | { 101 | to: '/training/Blocks/styles/', 102 | from: '/training/styles/', 103 | }, 104 | { 105 | to: '/training/Blocks/patterns/', 106 | from: '/training/patterns/', 107 | }, 108 | { 109 | to: '/training/Blocks/variations/', 110 | from: '/training/variations/', 111 | }, 112 | { 113 | to: '/training/Blocks/inner-blocks/', 114 | from: '/training/inner-blocks/', 115 | }, 116 | { 117 | to: '/training/Blocks/rich-text-formats/', 118 | from: '/training/rich-text-formats/', 119 | }, 120 | { 121 | to: '/training/Blocks/slot-fill/', 122 | from: '/training/slot-fill/', 123 | }, 124 | { 125 | to: '/training/Blocks/build-your-own/', 126 | from: '/training/build-your-own/', 127 | }, 128 | ] 129 | } 130 | ] 131 | ], 132 | 133 | themeConfig: 134 | /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ 135 | ({ 136 | colorMode: { 137 | defaultMode: 'light', 138 | disableSwitch: true, 139 | }, 140 | navbar: { 141 | title: 'Block Editor Best Practices', 142 | logo: { 143 | src: 'img/10up-logo-full.svg' 144 | }, 145 | items: [ 146 | { 147 | type: 'doc', 148 | docId: 'index', 149 | position: 'right', 150 | label: 'Reference', 151 | }, 152 | { 153 | type: 'doc', 154 | docId: 'index', 155 | position: 'right', 156 | label: 'Training', 157 | docsPluginId: 'training' 158 | }, 159 | { 160 | type: 'doc', 161 | docId: 'index', 162 | position: 'right', 163 | label: 'Guides', 164 | docsPluginId: 'guides' 165 | } 166 | ], 167 | }, 168 | announcementBar: { 169 | id: 'support_us', 170 | content: 171 | 'Have any questions or suggestions? Just open a discussion in this GitHub Repository', 172 | backgroundColor: '#fafbfc', 173 | textColor: '#091E42', 174 | isCloseable: false, 175 | }, 176 | footer: { 177 | style: 'light', 178 | links: [ 179 | { 180 | title: 'Docs', 181 | items: [ 182 | { 183 | label: 'Reference', 184 | to: '/reference', 185 | }, 186 | { 187 | label: 'Training', 188 | to: '/training', 189 | }, 190 | { 191 | label: 'Guides', 192 | to: '/guides', 193 | }, 194 | ], 195 | }, 196 | { 197 | title: 'Community', 198 | items: [ 199 | { 200 | label: 'Slack Channel (internal)', 201 | href: 'https://10up.slack.com/archives/C8Z3WMN1K', 202 | }, 203 | { 204 | label: 'GitHub Discussions', 205 | href: 'https://github.com/10up/gutenberg-best-practices/discussions/', 206 | } 207 | ], 208 | }, 209 | { 210 | title: 'Resources', 211 | items: [ 212 | { 213 | label: 'Block Components', 214 | href: 'https://github.com/10up/block-components', 215 | }, 216 | { 217 | label: 'Block Examples (internal)', 218 | href: 'https://github.com/10up/block-examples', 219 | }, 220 | { 221 | label: 'WP Scaffold', 222 | href: 'https://github.com/10up/wp-scaffold', 223 | }, 224 | ], 225 | }, 226 | ] 227 | }, 228 | prism: { 229 | theme: lightCodeTheme, 230 | darkTheme: darkCodeTheme, 231 | additionalLanguages: ['php', 'bash'], 232 | }, 233 | }), 234 | }; 235 | 236 | module.exports = config; 237 | -------------------------------------------------------------------------------- /guides/api-version-2.md: -------------------------------------------------------------------------------- 1 | # A Quick Guide for Gutenberg API Version 2 2 | 3 | First of all there is no need to freak out. I know API Version 2 sounds scary like everything is changing but that is not the case. In fact if you don't want to nothing has to change. API Version 2 is opt in and therefore does not impact anything if you don't want it to. But there are good reasons why you may want to use it. 4 | 5 | ## Benefits 6 | 7 | API Version two allows us to get rid of the additional wrapping div in the markup of the editor. Which makes matching the frontend styling in the editor much easier. 8 | 9 | ```html title="Block Markup - API Version 1" 10 |
11 |
12 |

Hello World

13 |
14 |
15 | ``` 16 | 17 | ```html title="Block Markup - API Version 2" 18 |
19 |

Hello World

20 |
21 | ``` 22 | 23 | ## Usage 24 | 25 | In order get that benefit there are two things that we need to do. 26 | 27 | 1. Set the `apiVersion` property to 2 in the block registration object 28 | 2. use the `useBlockProps` hook to get the attributes needed for the block in the editor 29 | 30 | ```json title="block.json" 31 | { 32 | "name": "example-block/api-version-two", 33 | "title": "Example Block - APIv2", 34 | "description": "API Version 2", 35 | "icon": "block-default", 36 | "category": "common", 37 | // highlight-start 38 | "apiVersion": 2, 39 | // highlight-end 40 | } 41 | ``` 42 | 43 | ```js title="edit.js" 44 | // highlight-start 45 | import { useBlockProps } from '@wordpress/block-editor'; 46 | // highlight-end 47 | 48 | export function BlockEdit() { 49 | // highlight-start 50 | const blockProps = useBlockProps(); 51 | // highlight-end 52 | 53 | return ( 54 | // highlight-start 55 |
56 | // highlight-end 57 |

Hello World

58 |
59 | ) 60 | }; 61 | ``` 62 | 63 | And that's it. If you are using static rendering you need to call `useBlockProps.save()` in your save method but for dynamic blocks (php rendering) nothing changes. 64 | 65 | ## Next steps 66 | 67 | Now that you are using API Version two you can also improve the markup of inner blocks areas. With the `useInnerBlocksProps` hook you can take complete control over the markup of inner block areas and therefore match the markup between the frontend and the editor. 68 | 69 | ```js title="edit.js" 70 | import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor'; 71 | 72 | export function BlockEdit() { 73 | const blockProps = useBlockProps(); 74 | const innerBlockProps = useInnerBlocksProps(); 75 | 76 | return ( 77 |
78 |
79 |
80 | ) 81 | }; 82 | ``` 83 | 84 | But you can take that even further. You don't even need to have them as two separate elements. You can pass blockProps as the first argument to `useInnerBlocksProps` giving you this: 85 | 86 | ```js title="edit.js" 87 | import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor'; 88 | 89 | export function BlockEdit() { 90 | const blockProps = useBlockProps(); 91 | const innerBlockProps = useInnerBlocksProps(blockProps); 92 | 93 | return ( 94 |
95 | ) 96 | }; 97 | ``` 98 | 99 | The output created by that in the editor is this: 100 | 101 | ```html 102 |
103 |

104 | Hello World 105 |

106 |
107 | ``` 108 | 109 | This is a huge win because you are now able to completely replicate the markup of the frontend in the editor. Which makes styling the editor like the frontend so much easier. 110 | 111 | ## Links 112 | 113 | - [Inner Blocks Reference](../reference/Blocks/inner-blocks) 114 | - [Block Editor Handbook - API Version Reference](https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/block-api/block-api-versions.md) 115 | -------------------------------------------------------------------------------- /guides/choose-your-adventure.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: How to Choose what to Build 3 | --- 4 | 5 | # A Complete Guide on How to Choose what to Build 6 | 7 | ![Decide how to build a block](../static/img/what-to-build.png) 8 | 9 | The two first and most important questions to answer before diving in and choosing how to implement a design in WordPress are: 10 | 11 | 1. _**"Who is going to use this?"**_ - as in who are the people that are going to be doing content entry 12 | 2. _**"What are their goals?"**_ - as in do they want to share information in a strict format or do they need flexibility over the page design 13 | 14 | When you are building editorial experiences it is important to understand the goals of the people you are crafting these experiences for. There are clients who _just_ want to share their content and don't want to worry about how things are going to look. That is what they hired us for. And there are other clients that essentially want a page builder that has their defaults set but gives them freedom to design every aspect of their page. 15 | ![Scale from Simplicity to Flexibility with Client needs leaning towards simplicity](../static/img/flexibility-scale.png) 16 | 17 | This also isn't a black or white question. Most clients fall somewhere in between these two extremes. Nevertheless it is crucial to get these questions answered as early in the process as possible in order to make the right editorial decisions for the client. 18 | 19 | ## Atomic vs. Molecular structures 20 | 21 | Depending on what the answer to these first questions is, you can figure out whether your client needs a system that is super composable — built out of individual atomic pieces that they then can use to build whatever they want — or whether they need more structure. 22 | 23 | Looking at this design for example, there are several ways in which you can approach building this in the editor. 24 | ![Design Mockup showing a row with three boxes](../static/img/sample-design-boxes.png) 25 | 26 | ### 1. Using Core Blocks 27 | 28 | This design could be built entirely using core blocks. 29 | 30 | ```plaintext 31 | Group 32 | Heading 33 | Columns 34 | Column 35 | Image 36 | Heading 37 | Paragraph 38 | Column 39 | Image 40 | Heading 41 | Paragraph 42 | Column 43 | Image 44 | Heading 45 | Paragraph 46 | ``` 47 | 48 | ### 2.a Using Core Blocks + a Block Pattern 49 | 50 | You can easily enhance the editor experience by also adding a block pattern for this arrangement of core blocks. This allows editors to quickly and easily create this section whilst leaving them the flexibility to change everything to their liking. 51 | 52 | :::caution 53 | As mentioned in the [block pattern reference](../reference/Patterns/overview) patterns have no connection to what created them. So any updates you make to the pattern in the future will not update instances that were created before you updated it. 54 | ::: 55 | 56 | ### 2.b Using Core Blocks + a Block Pattern with Restrictions 57 | 58 | If you want to lock down the abilities of the editors a bit more you can use the block locking API to lock down certain aspects of the editorial experience. You could, for example, remove the ability for them to move & remove any of the items within each of the columns. Therefore they would no longer be able to move the icon underneath the heading or remove the heading altogether. You can also lock down the list of allowed blocks of the column so that the editor can only insert images, headings, paragraphs and lets say a button. Or nothing at all. 59 | 60 | ### 3. Building a Custom Icon Card Block 61 | 62 | There are also valid reasons why the client needs an even more locked down experience. If the spacing needs to be just right and editors should not have to think about needing to design at all this can also be built as a _simple_ Icon Card Block. The block itself would consist of an icon picker and two `RichText` fields. With maybe an option to change the color theme of the entire card. 63 | 64 | This Icon Card block can then be inserted inside the columns block with a heading placed above. 65 | 66 | :::info 67 | The custom block should also get a block pattern created to make it even easier to insert the entire design on the page. Since we build dynamic blocks at 10up we now also don't have the issue of not being able to update markup in patterns anymore. Because we can update the markup of our dynamic block which gets reflected everywhere. 68 | ::: 69 | -------------------------------------------------------------------------------- /guides/extend-a-core-block.md: -------------------------------------------------------------------------------- 1 | # Extending a Core Block 2 | 3 | Something that comes up all the time is needing to extend a core block to add your own options to it. A common example is adding lightbox functionality to the core group block. So how can you approach this problem? 4 | 5 | ## Defining what you need to do 6 | 7 | In order to add your new options to another block you need to do a few things: 8 | 9 | - Add the attributes that you want to store to the block 10 | - Add the UI for the new option to the block 11 | - Add the new property / class name to the block in the editor 12 | - Add the new property / class name to the block on the frontend 13 | 14 | To simplify all these steps the [`@10up/block-components`](https://github.com/10up/block-components) library has a handy function called [`registerBlockExtension`](https://github.com/10up/block-components/tree/develop/api/register-block-extension) which allows you to do all these things. 15 | 16 | ### Defining new attributes 17 | 18 | Since you want to add new attributes to the block you need to create a new object that defines the new attributes you want to add. 19 | 20 | ```js 21 | const newAttributes = { 22 | isLightbox: { 23 | type: 'boolean', 24 | default: false 25 | }, 26 | focalPoint: { 27 | type: 'object', 28 | default: { 29 | x: 0.5, 30 | y: 0.5, 31 | }, 32 | }, 33 | }; 34 | ``` 35 | 36 | ### Create Class Name generator 37 | 38 | In order to add the new class name to the block wrapper element both in the editor and the frontend you need to create a function that generated the appropriate class name based on the value of the attributes. In this case you will want to add the `is-lightbox` class only when the `isLightbox` attribute is set to `true`. 39 | 40 | ```js 41 | function generateClassName( attributes ) { 42 | const { isLightbox } = attributes; 43 | let className = ''; 44 | if ( isLightbox ) { 45 | className = 'is-lightbox'; 46 | } 47 | return className; 48 | } 49 | ``` 50 | 51 | :::caution 52 | If you extend dynamic blocks that don't store their markup in the database you will need to replicate the logic to add the class name on the frontend manually in php. 53 | ::: 54 | 55 | ### Create Inline Style generator 56 | 57 | In certain use cases adding inline styles for a specific extended block may be a cleaner approach than adding a class to your block. In this use case the new inlineStyleGenerator function to generate inline styles that should be passed to the extended block. 58 | 59 | ```js 60 | function generateInlineStyle(attributes) { 61 | const { focalPoint } = attributes; 62 | let style = { objectPosition: '50% 50%' }; 63 | if (focalPoint) { 64 | style = { objectPosition: `${focalPoint.x * 100}% ${focalPoint.y * 100}%` }; 65 | } 66 | return style; 67 | } 68 | ``` 69 | 70 | ### Create Editor User Interface 71 | 72 | Finally you will need to define the editor UI you want for the new setting. This again is similar to how you would define it for a custom block. You create a react component that gets passed all the props from the block editor and you can use slots like the `InspectorControls` or the `BlockControls` to place your new settings into the sidebar or toolbar of the block. 73 | 74 | ```js 75 | function LightboxBlockEdit( props ) { 76 | const { attributes, setAttributes } = props; 77 | const { isLightbox, focalPoint, url } = attributes; 78 | 79 | return ( 80 | 81 | 82 | setAttributes({ isLightbox: value }) } 86 | /> 87 | setAttributes({ focalPoint: value })} 91 | url={url} 92 | /> 93 | 94 | 95 | ); 96 | } 97 | ``` 98 | 99 | ### Connecting the different Pieces 100 | 101 | Now that you have created these new attributes, class name generator function and editor ui you can pass all these values as options to the `registerBlockExtension` api. 102 | 103 | ```js 104 | import { registerBlockExtension } from '@10up/block-components'; 105 | 106 | registerBlockExtension( 107 | `core/gallery`, 108 | { 109 | extensionName: 'lightbox', 110 | attributes: newAttributes, 111 | classNameGenerator: generateClassName, 112 | inlineStyleGenerator: generateInlineStyle, 113 | Edit: LightboxBlockEdit 114 | } 115 | ); 116 | ``` 117 | 118 | Alternatively if the class name generator or inline style generator is not being used you can pass it an empty function with a return of null. 119 | 120 | ```js 121 | import { registerBlockExtension } from '@10up/block-components'; 122 | 123 | registerBlockExtension( 124 | `core/gallery`, 125 | { 126 | extensionName: 'lightbox', 127 | attributes: newAttributes, 128 | classNameGenerator: () => null, 129 | inlineStyleGenerator: ()=> null, 130 | Edit: LightboxBlockEdit 131 | } 132 | ); 133 | ``` 134 | -------------------------------------------------------------------------------- /guides/external-libraries.md: -------------------------------------------------------------------------------- 1 | # Working with External Libraries in Custom Blocks 2 | 3 | We can sometimes find ourselves with the need to integrate a 3rd party library into a Custom Block. In a recent example, I needed to integrate an interactive Map into the editor. The Map itself would be rendered using [D3.js](https://d3js.org). It should give editors the ability to change a percentage value for every state of the United States which would correlate to a color coating of that state. 4 | 5 | ![Map of the United States with some states highlighted](../static/img/dynamic-map-frontend.png) 6 | 7 | Our goal was as always to have the editor match the frontend as much as possible. So in this case we wanted the map to fully render in the editor and provide `RangeControl` sliders in the sidebar for each state in order to change the percentage value between 0 and 15 in the blocks settings sidebar. 8 | 9 | ## How do you render some non react content inside a react application? 10 | 11 | In order to render any non react elements inside a React document we need to get access to the actual DOM Node in the browser. The easiest and safest way to do that in modern react is using a `ref`. 12 | 13 | ```jsx 14 | import { useBlockProps } from '@wordpress/block-editor'; 15 | import { useRef } from '@wordpress/element'; 16 | 17 | function BlockEdit(props) { 18 | const blockProps = useBlockProps(); 19 | const ref = useRef(); 20 | 21 | return ( 22 |
23 |
24 |
25 | ); 26 | } 27 | ``` 28 | 29 | For [D3.js](https://d3js.org) specifically you select the DOM Node using the `d3.select` function in order to then manipulate the content further using all the helpers build into D3. 30 | 31 | Because of that we wrote all our render functions for D3 in a way where you needed to pass the selected root element into the render function. 32 | 33 | ```js 34 | function renderMap( rootElement ) { 35 | ... 36 | } 37 | ``` 38 | 39 | from there we were able to call this function both on the frontend and in the editor by passing the relevant node into the render function. 40 | 41 | ```jsx title="edit.js" 42 | import { useBlockProps } from '@wordpress/block-editor'; 43 | import { useRef, useEffect } from '@wordpress/element'; 44 | import * as d3 from 'd3'; 45 | 46 | import { renderMap } from './render-map'; 47 | 48 | function BlockEdit(props) { 49 | const blockProps = useBlockProps(); 50 | const ref = useRef(); 51 | 52 | useEffect( () => { 53 | if ( ref.current ) { 54 | const d3RootElement = d3.select(ref.current); 55 | renderMap( d3RootElement ); 56 | } 57 | }, []) 58 | 59 | return ( 60 |
61 |
62 |
63 | ); 64 | } 65 | ``` 66 | 67 | ```js title="frontend.js" 68 | import * as d3 from 'd3'; 69 | 70 | import { renderMap } from './render-map'; 71 | 72 | const mapBlocks = document.querySelectorAll( '.wp-block-namespace-map' ); 73 | mapBlocks.forEach( mapBlock => { 74 | const mapElement = mapBlock.querySelector( '.map' ); 75 | const d3RootElement = d3.select( mapElement ); 76 | renderMap( d3RootElement ); 77 | } ); 78 | ``` 79 | 80 | ## Making it dynamic 81 | 82 | At this point we had the static map rendering on both the frontend and in the editor. All that was left to do was create some dynamic data that we saved to the blocks attributes and then pass them in as the second argument to our `renderMap` function. All we need to do in order to ensure that the editor view re-renders every time the attributes change is to make sure the relevant attributes get added to the dependency array of the `useEffect` hook. 83 | 84 | ```jsx 85 | import { useBlockProps } from '@wordpress/block-editor'; 86 | import { useRef, useEffect } from '@wordpress/element'; 87 | import * as d3 from 'd3'; 88 | 89 | import { renderMap } from './render-map'; 90 | 91 | function BlockEdit(props) { 92 | const { attributes } = props; 93 | const { stateConcentration } = attributes; 94 | 95 | const blockProps = useBlockProps(); 96 | const ref = useRef(); 97 | 98 | useEffect( () => { 99 | if ( ref.current ) { 100 | const d3RootElement = d3.select(ref.current); 101 | renderMap( 102 | d3RootElement, 103 | { concentration: stateConcentration } 104 | ); 105 | } 106 | }, [ stateConcentration ]) 107 | 108 | return ( 109 |
110 |
111 |
112 | ); 113 | } 114 | ``` 115 | 116 | ![Dynamic Map Editor View](../static/img/dynamic-map-editor-view.png) 117 | 118 | ## Making it reusable 119 | 120 | In this case we actually ended up having the legend above the map and the markers for the cities be their own individual SVGs rendered by D3 and so we created a little helper react hook to make it easier working with the refs. 121 | 122 | ```js title="useD3.js" 123 | import { useEffect, useRef } from '@wordpress/element'; 124 | import * as d3 from 'd3'; 125 | 126 | export const useD3 = (callback, dependencies = []) => { 127 | const ref = useRef(); 128 | 129 | useEffect(() => { 130 | if ( ref.current ) { 131 | callback(d3.select(ref.current)); 132 | } 133 | }, dependencies); 134 | 135 | return ref; 136 | } 137 | ``` 138 | 139 | ## Other examples 140 | 141 | You can see another example of this by looking at the Apple Maps Block plugin that 10up has. We use a [similar approach there to get the map rendered in the editor](https://github.com/10up/maps-block-apple/blob/0b128ee79d1f67aca986ccc865584d179bc2c98a/src/edit.js#L25-L56). 142 | -------------------------------------------------------------------------------- /guides/including-frontend-javascript-with-a-block.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: Including Frontend JS with a Block 3 | --- 4 | 5 | # Including Frontend JavaScript with a Block 6 | 7 | Sometimes we need to include frontend JavaScript with a block. If it's only a small bit of CSS it's not a big issue to load that script on every page with the main frontend JavaScript bundle of the site. 8 | 9 | However, as soon as you start to load larger libraries or a lot of custom JavaScript that is only needed whenever a specific block is present on the page it is bad for performance to always load that entire JS on every single page. 10 | 11 | There are two ways we can solve this: 12 | 13 | 1. Use [dynamic imports (Webpack code splitting)](https://webpack.js.org/guides/code-splitting/) 14 | 2. Only enqueue block-specific JS when the block is present on the page 15 | 16 | In this guide, we are taking a look at the second option as it often is the easier and more robust one of the two. 17 | 18 | ## Only enqueueing block-specific JS when the block is present on the page 19 | 20 | To have a JavaScript file that only gets enqueued on the page if the block is present, we can define a `viewScript` in the `block.json` file. This `viewScript` can either be a relative file path to the JS file or a script handle that should get enqueued. 21 | 22 | ```json title="block.json" 23 | { 24 | "apiVersion": 2, 25 | "name": "namespace/example", 26 | "title": "Example Block", 27 | "editorScript": "file:./index.js", 28 | // highlight-next-line 29 | "viewScript": "file:./view.js" 30 | } 31 | ``` 32 | 33 | This automatically takes the JS file that is located at the relative file path and registers it using the `wp_register_script` function. The script gets the handle `namespace-example-view-script`. The handle is generated using the block namespace, followed by the block name, with the suffix `-view-script` added at the end. 34 | 35 | Every time the block gets used anywhere, WordPress will make sure to enqueue all the `viewScript` scripts and load them after the markup of the block. So you don't even need to check for any dom-ready event but can get started querying for the element right away. 36 | 37 | :::note 38 | WordPress expects a file that is provided via a relative file path to also have a `.asset.php` file next to it with the script dependencies and generated version number. Both `@wordpress/scripts` and `10up-toolkit` do this automatically for you using the `@wordpress/dependency-extraction-webpack-plugin`. 39 | ::: 40 | 41 | ### Enqueueing additional external dependencies 42 | 43 | If your script relies on additional non-WordPress dependencies like a 3rd party library that cannot be installed via NPM you can also pass multiple values to the `viewScript`. Each value can either be a relative file path starting with `file:` or the handle of a registered script. 44 | 45 | ```json title="block.json" 46 | { 47 | "apiVersion": 2, 48 | "name": "namespace/example", 49 | "title": "Example Block", 50 | "editorScript": "file:./index.js", 51 | // highlight-next-line 52 | "viewScript": [ "file:./view.js", "my-custom-view-script-handle"] 53 | } 54 | ``` 55 | 56 | :::info 57 | Since WordPress 6.1 Dynamic blocks also automatically enqueue the JS file on all pages where the block is being used. Previously that only worked for static blocks and dynamic ones needed to manually call `wp_enqueue_script` with the auto-generated view script handle. 58 | ::: 59 | -------------------------------------------------------------------------------- /guides/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 0 3 | sidebar_label: Introduction 4 | --- 5 | 6 | # Guides 7 | 8 | ![Deep Dive](../static/img/deep-dive.png) 9 | 10 | This section of the Gutenberg Best Practices is meant as a collection of individual deep dive articles. 11 | 12 | You are also welcome to contribute articles to this guide :) Just create a Pull Request on the GitHub repository. 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gutenberg-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "^3.7.0", 18 | "@docusaurus/plugin-client-redirects": "^3.7.0", 19 | "@docusaurus/plugin-content-docs": "^3.7.0", 20 | "@docusaurus/preset-classic": "^3.7.0", 21 | "@docusaurus/theme-classic": "^3.7.0", 22 | "@docusaurus/theme-search-algolia": "^3.7.0", 23 | "@easyops-cn/docusaurus-search-local": "^0.44.5", 24 | "@mdx-js/react": "^3.0.1", 25 | "clsx": "^2.1.1", 26 | "prism-react-renderer": "^2.3.1", 27 | "react": "^18.3.1", 28 | "react-dom": "^18.3.1" 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.5%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 10up Gutenberg Best Practices 2 | 3 | This repository houses the source code for the [10up Gutenberg Best Practices website](https://gutenberg.10up.com). 4 | 5 | The content of this site is grouped into three different sections: Training, Reference, and Guides. 6 | 7 | ## Training 8 | The training section is meant for hands on learning. By covering the most common development topics in order this will get anyone up to speed with how we build experiences for the editor at 10up. 9 | 10 | ## Reference 11 | The reference section on the other hand is a place for detailed reference explications on everything surrounding the block editor. 12 | 13 | ## Guides 14 | Guides are one off articles that go into depth on one particular topic. 15 | 16 | 17 | --- 18 | 19 | ## Questions / Suggestions? 20 | If you have questions about the material covered or have any suggestions for additional topics please create a new Discussion in the discussions tab here in GutHub. 21 | 22 | ## Contributing 23 | Issues & PR's are always welcome :) To build this site locally all you need to do is run `npm ci` and `npm start` 24 | -------------------------------------------------------------------------------- /reference/01-Fundamentals/the-editor.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: The Block Editor 3 | sidebar_position: 1 4 | --- 5 | 6 | # A Recap of the WordPress Block Editor 7 | 8 | ![Block Editor Sketch](../../static//img/block-anatomy-anotated.png) 9 | 10 | The editor is the main interface through which one interacts with their website. It is the gateway through which you can share your message with the world. So the editorial experience is essential in creating a pleasant supportive experience that allows you to focus just on what you want to share. 11 | 12 | There are several different instances of the editor throughout the WordPress admin interface. The most common one being the **post editor**. it is what you are probably thinking of straight away. The post editor is where you create new or edit posts/pages or any other custom post type. 13 | 14 | But there are also other instances of the block editor being used throughout the admin interface. There is the site editor also referred to as the template editor which was introduced in WordPress 5.8 as the first piece of the full-site-editing project. Finally, for now, there is the widget editor which now also runs powered by the block editor. 15 | 16 | ## Anatomy of the Block Editor 17 | 18 | No matter which instance of the editor you are working with the fundamental anatomy always stays the same. There always is the main editing canvas, a top toolbar and a settings sidebar on the side. 19 | 20 | ### Top Toolbar 21 | 22 | ![Block Editor Toolbar](../../static//img/gutenberg-toolbar.png) 23 | The top toolbar is a place for page level tools and interface related toggles. On the left side you can open the block inserter, switch between different tools, get an overview of the structure of your post, and open the list view. On the right side you can save, publish, update or preview the post and toggle the main settings sidebar. Plugins can also register their own plugin panels to show up here. Finally through the kebab-menu in the right corner you can access additional options & settings panels. 24 | 25 | ### Sidebar 26 | 27 | ![Block Editor Sidebar](../../static//img/gutenberg-sidebar.png) 28 | The settings sidebar has two tabs. The first one is for page level controls. This is where any settings that impact the overall page are placed. 29 | 30 | The second tab is for the currently selected block. It houses advanced controls that allow you to control the appearance of the selected block. 31 | 32 | ![Sidebar Types](../../static//img/block-editor-sidebar-panels.png) 33 | 34 | In either sidebar type there are two different types of Panels. Ones that are collapsible and ones that allow you to toggle on/off the controls within that section. 35 | -------------------------------------------------------------------------------- /reference/02-Themes/block-based-templates.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: Block Templates 3 | sidebar_position: 3 4 | --- 5 | 6 | # Block Based Templates 7 | 8 | In block-based-themes, all parts of the theme are built out of blocks. So instead of just the content area, all templates and template parts are also built via blocks. 9 | 10 | These block templates follow the same template hierarchy as traditional themes. But instead of `index.php`, `single.php`, `page.php`, etc. we now have `templates/index.html`, `templates/single.html`, `templates/page.html`, etc. 11 | 12 | ## How to start using block templates 13 | 14 | Technically you can start using block templates in any theme. So even in a classic theme you could start and introduce a new block based template by creating a `templates` folder in the theme root and then adding a new template file to it. 15 | 16 | The lookup for which template gets used uses the same logic as the classic theme hierarchy just with the added complexity that it looks for `.html` files first and then falls back to the old `.php` files if it cannot find a `.html` version. 17 | 18 | :::tip 19 | The best experience however is when starting on a new build where all templates are built as block templates from the beginning. Because otherwise if you mix the two you will have to do manual work to share your header / footer block based template parts between the two systems. 20 | ::: 21 | 22 | ## Anatomy of a block template 23 | 24 | A block template is a regular HTML file that contains the block markup. In order to work properly however the markup needs to be 100% valid block markup including the `` comments. 25 | 26 | :::caution 27 | You cannot just use arbitrary HTML in a block template. It needs to be valid block markup. 28 | ::: 29 | 30 | ## Authoring block templates 31 | 32 | Because they need to contain valid block markup, hand authoring these templates can be quite cumbersome. Therefore it is recommended to create the block template in the site editor itself and use the [Create Block Theme plugin](https://wordpress.org/plugins/create-block-theme/) to save all the changes you made in the editor back to the theme. 33 | 34 |
35 | How do I translate strings in these `.html` files? 36 | 37 | Because these are just HTML files, you can't use the `__()` function to translate strings directly. The workaround that has been established for this is to put all the contents of the template into a pattern instead which can be a `.php` file and then use the `__()` function in there. This pattern can then be referenced in the `.html` file via the `core/pattern` block. 38 | 39 | ```php title="patterns/single-post.php" 40 | 48 | 49 | 50 |

51 | 52 | ``` 53 | 54 | ```html title="templates/single.html" 55 | 56 | ``` 57 | 58 | Luckily, the Create Block Theme plugin does this for you automatically if you check the "Translate strings" checkbox when saving the template. 59 | 60 |
61 | 62 | ## Previewing the template in the editor 63 | 64 | One of the benefits of block templates is that unlike traditional templates, you can preview them directly in the editor. This makes it much easier for editors to see how their changes will look on the frontend. 65 | 66 | In the sidebar of any post you can find a setting to switch between the different templates that are available for the post type. Clicking on this dropdown also reveals a "Show Template" button that will enable the preview. 67 | 68 | We can also enable this setting programmatically via this little snippet: 69 | 70 | ```js 71 | import { registerPlugin } from '@wordpress/plugins'; 72 | import { useDispatch, useSelect } from '@wordpress/data'; 73 | import { useEffect } from '@wordpress/element'; 74 | import { store as editorStore } from '@wordpress/editor'; 75 | 76 | // Define post types that show the full template locked site editor by default. 77 | const TEMPLATE_LOCKED_POST_TYPES = [ 78 | 'page', 79 | 'post', 80 | ]; 81 | 82 | registerPlugin('set-default-template-rendering-mode', { 83 | render: function Edit() { 84 | const { setRenderingMode } = useDispatch(editorStore); 85 | 86 | const [postType] = useSelect((select) => [select(editorStore).getCurrentPostType()], []); 87 | 88 | useEffect(() => { 89 | if (!TEMPLATE_LOCKED_POST_TYPES.includes(postType)) { 90 | return; 91 | } 92 | setRenderingMode('template-locked'); 93 | }, [setRenderingMode, postType]); 94 | 95 | return null; 96 | }, 97 | }); 98 | ``` 99 | 100 | _We are working on actually allowing you to set the default rendering mode as a property of any post type server side. You can [follow along the progress on GitHub.](https://github.com/WordPress/gutenberg/issues/58038)_ 101 | 102 | ### Allowing users to edit elements that are part of the template 103 | 104 | When the "Show Preview" option is enabled, the template itself is rendered but all the blocks are locked completely and cannot be interacted with. They also don't show up in the list view. 105 | 106 | However, some core blocks get special treatment here. If you place the Post Title, or Post Featured Image blocks inside the template for example they will remain editable. This is because these blocks are not storing their settings in an attribute but rather modify some other property of the post object. And the only thing we can edit about them is the actual "content". 107 | 108 | So these "special" blocks get rendered in the `contentOnly` editing mode. 109 | 110 | We can filter which blocks get this special treatment via the `editor.postContentBlockTypes` filter: 111 | 112 | ```js 113 | import { addFilter } from '@wordpress/hooks'; 114 | 115 | addFilter('editor.postContentBlockTypes', 'namespace/identifier', (blockTypes) => { 116 | return [...blockTypes, 'namespace/my-block']; 117 | }); 118 | ``` 119 | 120 | If you want to learn more about making your block support the `contentOnly` rendering mode there is a great article on the WordPress Developer Block you should check out: [https://developer.wordpress.org/news/2024/11/05/how-to-add-content-only-editing-support-to-a-block/](https://developer.wordpress.org/news/2024/11/05/how-to-add-content-only-editing-support-to-a-block/) 121 | 122 | ## Choosing what's part of the template and what's not 123 | 124 | For a while there before block based themes, we moved more and more "template elements" such as page headers etc into the actual post content area because that was the only way to create a rich editing experience for these elements. 125 | 126 | However doing this came with several downsides such as: 127 | 128 | - In case of a redesign we now have that page header shored in thousands of individual posts and pages which makes updating it much harder. 129 | - We have to do custom work to ensure we are not duplicating the elements in the header when the post gets rendered in other contexts such as an RSS feed or Apple News. 130 | 131 | Now with the ability to preview the template and also have some individual elements inside these templates still be editable we can move these elements back into the template where they belong. 132 | -------------------------------------------------------------------------------- /reference/02-Themes/block-template-parts.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: Block Template Parts 3 | sidebar_position: 4 4 | --- 5 | 6 | # Block Template Parts 7 | 8 | Block based template parts are a big part of block based themes. But starting in WordPress 6.1 they can also be used by traditional WordPress themes. This reference will primarily focus on the utility of block based template parts in traditional themes. But many of the concepts do still apply in both contexts. 9 | 10 | ![Block Templates List](../../static//img/block-template-parts.png) 11 | 12 | ## What should block template parts be used for? 13 | 14 | Block template parts can be used to make any part of a website that isn't located within the content area of a post/page editable by blocks. Obvious examples of this are the site header or the site footer. But also any other elements such as the post author bio, a CTA that should get displayed site wide at the bottom of any page, or even thinks like modals and notifications can be build using this feature. 15 | 16 | All existing block template parts can be edited in the template part editor. This instance of the editor features a resizable canvas that makes easy to quickly verify that the template part works across breakpoints. 17 | 18 | ![Block Template Editor](../../static//img/block-template-part-editor.png) 19 | 20 | :::info 21 | The block template part editor also features an iframed editor. So the preview is actually accurate for what will happen on the frontend of the site. 22 | 23 | If you are building custom blocks that utilize JavaScript DOM manipulations you can use this guide to learn more about how to work with this iframed context: [https://make.wordpress.org/core/2021/06/29/blocks-in-an-iframed-template-editor/](https://make.wordpress.org/core/2021/06/29/blocks-in-an-iframed-template-editor/) 24 | ::: 25 | 26 | ## How to enable block template parts 27 | 28 | In order to enable block template parts in a traditional theme, the theme needs to opt in using the `block-template-parts` theme support. 29 | 30 | ```php title="function.php" 31 | function add_block_template_part_support() { 32 | // highlight-next-line 33 | add_theme_support( 'block-template-parts' ); 34 | } 35 | 36 | add_action( 'after_setup_theme', 'add_block_template_part_support' ); 37 | ``` 38 | 39 | ## Creating block template parts 40 | 41 | With that theme support added individual template parts can now be added by creating `html` files containing the block markup that should be used by default for the pattern. 42 | 43 | ```html title="/parts/footer.html" 44 | 45 |
46 | 47 |
48 | 49 |

Proudly Powered by WordPress

50 | 51 |
52 | 53 |
54 | 55 | ``` 56 | 57 | This `html` file just contains the same markup a block pattern would. 58 | 59 | :::tip 60 | It is easiest to create the template part in the editor itself and then use the copy feature to copy the markup of the block and paste it into the `html` file. 61 | ::: 62 | 63 | By default the template part will use the name of the html file as its label in the Block Template Part overview. This label can be customized via the `theme.json` file. 64 | 65 | ```json title="theme.json" 66 | { 67 | "version": 2, 68 | "templateParts": [ 69 | { 70 | "name": "footer", 71 | "title": "Site Footer", 72 | "area": "footer" 73 | } 74 | ] 75 | } 76 | ``` 77 | 78 | :::note 79 | The available `areas` are `header`, `footer`, and `uncategorized`. 80 | ::: 81 | 82 | ## Using block template parts inside of traditional themes 83 | 84 | To actually use this template part the theme author then needs to call the `block_template_part` function and pass the name of the template part as the first and only parameter. 85 | 86 | ```php title="footer.php" 87 | // highlight-next-line 88 | 89 | 90 | ``` 91 | 92 | ## Caveats with using block template parts 93 | 94 | Block template parts feature-wise are very similar to block based widget areas. Because the block based template parts feature has been build for the editor from the ground up it provides a better user experience and therefore should be preferred in most instances. 95 | 96 | Ideally both features shouldn't be used at the same time. 97 | 98 | There is an issue in WordPress 6.1 that [prevents shortcodes from getting used inside block based template areas](https://core.trac.wordpress.org/ticket/56780). 99 | 100 | ## Links 101 | 102 | - [Block-based “template parts” in traditional themes](https://make.wordpress.org/core/2022/10/04/block-based-template-parts-in-traditional-themes/) 103 | - [Adding block template parts in classic themes](https://developer.wordpress.org/themes/block-themes/converting-a-classic-theme-to-a-block-theme/#adding-block-template-parts-in-classic-themes) 104 | -------------------------------------------------------------------------------- /reference/02-Themes/fonts.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: Fonts 3 | sidebar_position: 5 4 | --- 5 | 6 | # Loading Fonts using `theme.json` 7 | 8 | ## Why use `theme.json` for controlling font loading? 9 | 10 | Using `theme.json` to handle the registration of fonts automatically makes sure the font files are properly enqueued everywhere in WordPress. It provides us with a simple API to control all aspects of how the font should get rendered and loaded. This also directly works for any iframed editors. 11 | 12 | ## How to register local font files 13 | 14 | Most of the time we want to load custom font files that we bundle with the theme itself on the server. This is for both performance and privacy reasons. We can avoid additional requests to external servers. 15 | 16 | Each font family we want to load needs to get it's own entry in the `settings.typography.fontFamilies` array. Each entry consists of: 17 | 18 | - `fontFamily` - Font Family declaration like in `css` 19 | - `name` - Human readable Name of the font family 20 | - `slug` - Computer readable Name of the font family 21 | - `fontFace` - Array of the individual font faces 22 | 23 | The `fontFace` itself is another array that houses each of the individual font faces of the family. So each font weight, style etc gets added here manually. 24 | 25 | - `fontFamily` - Name of the font family 26 | - `fontWeight` - Weight of the current font face 27 | - `fontStyle` - Style of the current font face 28 | - `fontDisplay` - Font Display of the current font face 29 | - `src` - Array of relative file paths to the individual font files of the current font face 30 | 31 | ```json 32 | { 33 | "settings": { 34 | "typography": { 35 | "fontFamilies": [ 36 | { 37 | "fontFamily": "FontName, sans-serif", 38 | "name": "FontName", 39 | "slug": "fontname", 40 | "fontFace": [ 41 | { 42 | "fontFamily": "FontName", 43 | "fontWeight": "100", 44 | "fontStyle": "normal", 45 | "fontDisplay": "block", 46 | "src": [ 47 | "file:./dist/fonts/fontname/fontname-thin.otf", 48 | "file:./dist/fonts/fontname/fontname-thin.woff", 49 | "file:./dist/fonts/fontname/fontname-thin.woff2" 50 | ] 51 | }, 52 | ... 53 | ] 54 | } 55 | ] 56 | } 57 | } 58 | } 59 | 60 | ``` 61 | 62 | The resulting output on the page will be an inline style tag with the various `@font-face` declarations: 63 | 64 | ```html 65 | 76 | ``` 77 | 78 | :::tip 79 | Most of the time you won't actually want to manually write all the JSON to add your fonts. Instead you can use the [Create Block Theme](https://wordpress.org/plugins/create-block-theme/) plugin to visually manage your fonts and then output the resulting `theme.json` file directly to your theme. 80 | ::: 81 | 82 | ## Font Library 83 | 84 | The font library is part of the Global Styles section inside the site editor of a block theme. It visually allows administrators to manage which fonts are installed on their site. 85 | 86 | The font library exposes available font collections in the UI. By default WordPress core includes a collection for Google Fonts. But any developer can add additional font collections in the same way using the `wp_register_font_collection` function. 87 | 88 | You can [learn more about registering custom font collections in the core documentation](https://make.wordpress.org/core/2024/03/14/new-feature-font-library/#adding-a-font-collection). 89 | 90 | :::caution 91 | On most client builds we don't actually want to rely on administrators adding random custom fonts. Because doing so can have very strong negative side effects on both the visual appearance of a site but even more importantly the performance of a site. Therefore the best practice is to already define the correct fonts in the themes `theme.json` file and even disable the font library UI via this filter: 92 | 93 | ```php 94 | function disable_font_library_ui( $editor_settings ) { 95 | $editor_settings['fontLibraryEnabled'] = false; 96 | return $editor_settings; 97 | } 98 | 99 | add_filter( 'block_editor_settings_all', 'disable_font_library_ui' ); 100 | ``` 101 | 102 | ::: 103 | -------------------------------------------------------------------------------- /reference/02-Themes/navigation.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: Navigation 3 | sidebar_position: 6 4 | --- 5 | 6 | # Navigation in Block-based Themes 7 | 8 | Block-based themes changed how navigation areas are built. Instead of using `wp_nav_menu()` in your theme, you now use blocks to create navigation areas. Each navigation menu now has a post in a hidden core post type called `wp_navigation`. These posts then contain the block markup for the navigation menu. 9 | 10 | ## Working with Navigation Menus 11 | 12 | The core navigation block stores the ID of the navigation post in its attributes. This way the block knows which navigation menu to display. 13 | 14 | Sadly, because of this reliance on the ID we lose the concept of navigation areas. In classic themes you could register a navigation area and then assign a menu to it in the Customizer. This is not possible anymore with block-based themes. 15 | 16 | That means we have to become a little creative when it comes to working with the ID's of navigation menus when working across multiple environments. 17 | 18 | There are two main problems we have to solve for: 19 | 20 | 1. The ID of the navigation menu changes when moving the site from one environment to another. 21 | 2. Selecting the Navigation Menu to use in the site editor will mean that the template / template-part will now be saved to the database and is locked out of code updates. 22 | 23 | We have a few options of working around these limitations: 24 | 25 | ### 1. Sync the navigation menus across all environments 26 | 27 | Create all the navigation menus on your production site and then export them to your development environment. This way the ID's will stay the same across all environments. 28 | Whilst this approach is the easiest to implement, it is not the most flexible. If you ever need to change the navigation menu on your development environment, you will have to export it again and import it on your production site. 29 | 30 | ### 2. Wrap the navigation block alone in a separate block based template part 31 | 32 | Doing this allows you to keep the settings of the navigation block separate from the rest of the template. This way you can change the navigation menu in the site editor without having to worry about the rest of the template being saved to the database. You still don't have the same navigation menu across all environments, but at least you can change it without having to export and import it. 33 | 34 | ### 3. Custom Navigation Block 35 | 36 | Something we have also done, is creating a custom navigation block that uses the same underlying logic of the core navigation post type etc. But instead of storing the ID of the navigation post in the block attributes, we store the slug of the navigation menu. This way we can easily change the navigation menu in the site editor without having to worry about the ID changing. 37 | 38 | ### 4. Custom Navigation Block that uses the old menu system 39 | 40 | Another option is to create a custom navigation block that uses the old menu system. This way you can still use the old menu editor to assign a menu to a navigation area. This approach does retain the familiarity of the old system, but it also means you are not using the new block-based system to its full potential. 41 | -------------------------------------------------------------------------------- /reference/02-Themes/styles.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: Styling 3 | sidebar_position: 1 4 | --- 5 | 6 | # How to properly include styles? 7 | 8 | First of all the goal we aim for here is parity between the editor and the frontend of the site. The stylesheet that gets loaded on the frontend of the site should also get loaded in the editor. The expectation is that the markup matches between both environments. 9 | 10 | ## The main stylesheet 11 | 12 | In order for our stylesheet to get loaded in the editor including all its variants like the Template Editor, Site Editor, and Widget Editor (some of which are already rendering the content in an iframe) we use the [`add_editor_style`](https://developer.wordpress.org/reference/functions/add_editor_style/) function. 13 | 14 | ```php 15 | function theme_setup() { 16 | // enables the support for editor styles 17 | add_theme_support( 'editor-styles' ); 18 | 19 | // loads the actual stylesheet 20 | // The path provided is relative to the themes root directory 21 | // you can also pass a URL for external stylesheets like a font etc. 22 | add_editor_style( 'dist/css/style.css' ); 23 | } 24 | add_action( 'after_setup_theme', 'theme_setup' ); 25 | ``` 26 | 27 | The stylesheets that get added via the `add_editor_style` function get automatically parsed by WordPress and get scoped to the `.editor-styles-wrapper` class. This means that you cannot target anything outside of the `.editor-styles-wrapper` in that stylesheet since core will override that. The parsed and transformed stylesheet then gets inlined in the editor. 28 | 29 | :::tip 30 | If you need to load custom fonts from an external source you also need to add a separate `add_editor_style` call for the stylesheet loading the font. 31 | ::: 32 | 33 |
34 | Enqueueing order on the page: 35 |

36 | 37 | Looking at the DOM we can see that the stylesheets get inlined in a particular order. It first loads the ones added via the `add_editor_style` function, followed by the inline styles generated from the values defined in [`theme.json`](./theme-json.md). 38 | 39 |

40 |
41 | 42 | In cases where you need to load custom style overrides for the **admin interface** in the editor you can still do that with the `enqueue_block_editor_assets` hook. If you need to load a stylesheet for content elements in the editor and `add_editor_style` isn't an option you can use the `enqueue_block_assets`. Assets enqueued on that hook get loaded both on the frontend and in the iframed editor. If you only want to load a stylesheet in the iframed editor you can wrap your enqueue statement in an `is_admin` check. 43 | 44 | ## Block specific styles 45 | 46 | If you have stylesheets that are specific to a particular block you **can** also add them via the `wp_enqueue_block_style` function that was introduced in WordPress 5.9. The stylesheets that you add here will also get loaded properly in all the various contexts where a block is being used. The only difference being that they are only loaded when the block is actually used. 47 | 48 | ```php 49 | function theme_setup() { 50 | // Same args used for wp_enqueue_style(). 51 | $args = [ 52 | 'handle' => 'my-theme-site-title', 53 | 'src' => get_theme_file_uri( 'assets/blocks/site-title.css' ), 54 | ]; 55 | 56 | // Add "path" to allow inlining asset if the theme opts-in. 57 | $args['path'] = get_theme_file_path( 'assets/blocks/site-title.css' ); 58 | 59 | // Enqueue asset. 60 | wp_enqueue_block_style( 'core/site-title', $args ); 61 | } 62 | add_action( 'after_setup_theme', 'theme_setup' ); 63 | ``` 64 | 65 | In order to actually only load the stylesheets for the blocks that are actually used on a page the theme also needs to opt into that behavior by hooking into the `should_load_separate_core_block_assets` filter. 66 | 67 | ```php 68 | add_filter( 'should_load_separate_core_block_assets', '__return_true' ); 69 | ``` 70 | 71 | ## Inlining small assets 72 | 73 | In some cases small stylesheets get loaded on WordPress sites. These stylesheets require the browser to make an additional request to get an asset, and while they benefit from caching, their small size doesn’t justify that extra request, and performance would improve if they were inlined. 74 | 75 | To that end, an inlining mechanism was implemented. This is an opt-in feature, and can be handled on a per-stylesheet basis. Internally, only assets that have data for `path` defined get processed, so to opt-in, a stylesheet can add something like this: 76 | 77 | ```php 78 | wp_style_add_data( $style_handle, 'path', $file_path ); 79 | ``` 80 | 81 | When a page gets rendered, stylesheets that have opted-in to get inlined get added to an array. Their size is retrieved using a `filesize` call (which is why the `path` data is necessary), and the array is then ordered by ascending size (smaller to larger stylesheet). We then start inlining these assets by going from smallest to largest, until a 20kb limit is reached. 82 | 83 | A filter is available to change that limit to another value, and can also be used to completely disable inlining. 84 | 85 | To completely disable small styles inlining: 86 | 87 | ```php 88 | add_filter( 'styles_inline_size_limit', '__return_zero' ); 89 | ``` 90 | 91 | To change the total inlined styles limit to 50kb: 92 | 93 | ```php 94 | add_filter( 'styles_inline_size_limit', function() { 95 | return 50000; // Size in bytes. 96 | }); 97 | ``` 98 | 99 | Inlining these styles happens by changing the `src` of the style to `false`, and then adding its contents as inline data. This way we avoid backwards-compatibility issues in themes and any additional styles attached to these stylesheets using `wp_add_inline_style` will still be printed. 100 | 101 | :::note 102 | Please note that if a stylesheet opts-in to get inlined, that is no guarantee that it will get inlined. 103 | 104 | If for example on a page there are 30 stylesheets that are 1kb each, and they all opt-in to be inlined, then only 20 of them will be converted from `` to ` 8 | 9 | 13 | 15 | 16 | -------------------------------------------------------------------------------- /static/img/applications-cta-blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/applications-cta-blank.png -------------------------------------------------------------------------------- /static/img/applications-cta-inserter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/applications-cta-inserter.png -------------------------------------------------------------------------------- /static/img/atomic-blocks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/atomic-blocks.png -------------------------------------------------------------------------------- /static/img/background-pattern-questionmarks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/background-pattern-questionmarks.png -------------------------------------------------------------------------------- /static/img/block-anatomy-anotated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-anatomy-anotated.png -------------------------------------------------------------------------------- /static/img/block-css-inlining.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-css-inlining.png -------------------------------------------------------------------------------- /static/img/block-deselected-state.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-deselected-state.jpg -------------------------------------------------------------------------------- /static/img/block-editor-sidebar-panels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-editor-sidebar-panels.png -------------------------------------------------------------------------------- /static/img/block-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-editor.png -------------------------------------------------------------------------------- /static/img/block-extenstions-after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-extenstions-after.png -------------------------------------------------------------------------------- /static/img/block-extenstions-before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-extenstions-before.png -------------------------------------------------------------------------------- /static/img/block-initial-setup-state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-initial-setup-state.png -------------------------------------------------------------------------------- /static/img/block-lock-activated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-lock-activated.png -------------------------------------------------------------------------------- /static/img/block-locking-ui.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-locking-ui.gif -------------------------------------------------------------------------------- /static/img/block-migration.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-migration.jpg -------------------------------------------------------------------------------- /static/img/block-pattern-add-new-modal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-pattern-add-new-modal.jpg -------------------------------------------------------------------------------- /static/img/block-pattern-bindings-finished-example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-pattern-bindings-finished-example.jpg -------------------------------------------------------------------------------- /static/img/block-pattern-bindings-post-meta-example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-pattern-bindings-post-meta-example.jpg -------------------------------------------------------------------------------- /static/img/block-pattern-call-to-action-example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-pattern-call-to-action-example.jpg -------------------------------------------------------------------------------- /static/img/block-pattern-save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-pattern-save.png -------------------------------------------------------------------------------- /static/img/block-pattern-synced-edit-original.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-pattern-synced-edit-original.jpg -------------------------------------------------------------------------------- /static/img/block-pattern-synced-override-enabled.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-pattern-synced-override-enabled.jpg -------------------------------------------------------------------------------- /static/img/block-patterns-structure-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-patterns-structure-diagram.png -------------------------------------------------------------------------------- /static/img/block-reference-sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-reference-sketch.png -------------------------------------------------------------------------------- /static/img/block-selected-state.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-selected-state.jpg -------------------------------------------------------------------------------- /static/img/block-settings-sidebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-settings-sidebar.png -------------------------------------------------------------------------------- /static/img/block-template-part-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-template-part-editor.png -------------------------------------------------------------------------------- /static/img/block-template-parts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-template-parts.png -------------------------------------------------------------------------------- /static/img/block-toolbar-groups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-toolbar-groups.png -------------------------------------------------------------------------------- /static/img/block-toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-toolbar.png -------------------------------------------------------------------------------- /static/img/block-transforms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-transforms.png -------------------------------------------------------------------------------- /static/img/block-variations-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/block-variations-example.png -------------------------------------------------------------------------------- /static/img/columns-block-variations-picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/columns-block-variations-picker.png -------------------------------------------------------------------------------- /static/img/content-only-pattern-modify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/content-only-pattern-modify.png -------------------------------------------------------------------------------- /static/img/content-only-pattern.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/content-only-pattern.gif -------------------------------------------------------------------------------- /static/img/content-only-pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/content-only-pattern.png -------------------------------------------------------------------------------- /static/img/contrib-block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/contrib-block.png -------------------------------------------------------------------------------- /static/img/contrib-block@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/contrib-block@2x.png -------------------------------------------------------------------------------- /static/img/contrib-docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/contrib-docs.png -------------------------------------------------------------------------------- /static/img/contrib-docs@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/contrib-docs@2x.png -------------------------------------------------------------------------------- /static/img/contrib-scaffold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/contrib-scaffold.png -------------------------------------------------------------------------------- /static/img/contrib-scaffold@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/contrib-scaffold@2x.png -------------------------------------------------------------------------------- /static/img/core-embed-variations-inserter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/core-embed-variations-inserter.png -------------------------------------------------------------------------------- /static/img/core-image-rounded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/core-image-rounded.png -------------------------------------------------------------------------------- /static/img/core-image-slightly-rounded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/core-image-slightly-rounded.png -------------------------------------------------------------------------------- /static/img/core-image-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/core-image-square.png -------------------------------------------------------------------------------- /static/img/core-image-styles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/core-image-styles.png -------------------------------------------------------------------------------- /static/img/core-image-variations.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/core-image-variations.jpg -------------------------------------------------------------------------------- /static/img/core-rich-text-formats-screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/core-rich-text-formats-screenshot.jpg -------------------------------------------------------------------------------- /static/img/css-file-names.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/css-file-names.png -------------------------------------------------------------------------------- /static/img/cta-block-style.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/cta-block-style.png -------------------------------------------------------------------------------- /static/img/cta-block-thick-border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/cta-block-thick-border.png -------------------------------------------------------------------------------- /static/img/cta-complete-frontend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/cta-complete-frontend.png -------------------------------------------------------------------------------- /static/img/cta-complete-with-control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/cta-complete-with-control.png -------------------------------------------------------------------------------- /static/img/cta-complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/cta-complete.png -------------------------------------------------------------------------------- /static/img/data-api-core-stores.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/data-api-core-stores.png -------------------------------------------------------------------------------- /static/img/data-api-loading-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/data-api-loading-error.png -------------------------------------------------------------------------------- /static/img/data-api-wordpress-db.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/data-api-wordpress-db.png -------------------------------------------------------------------------------- /static/img/deep-dive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/deep-dive.png -------------------------------------------------------------------------------- /static/img/design-styleguide-spacing-scale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/design-styleguide-spacing-scale.png -------------------------------------------------------------------------------- /static/img/dimensions-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/dimensions-panel.png -------------------------------------------------------------------------------- /static/img/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/docusaurus.png -------------------------------------------------------------------------------- /static/img/dynamic-map-editor-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/dynamic-map-editor-view.png -------------------------------------------------------------------------------- /static/img/dynamic-map-frontend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/dynamic-map-frontend.png -------------------------------------------------------------------------------- /static/img/embed-block-variations-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/embed-block-variations-overview.png -------------------------------------------------------------------------------- /static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/favicon.ico -------------------------------------------------------------------------------- /static/img/flexibility-scale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/flexibility-scale.png -------------------------------------------------------------------------------- /static/img/fse-template-hierarchy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/fse-template-hierarchy.png -------------------------------------------------------------------------------- /static/img/global-styles-input-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/global-styles-input-output.png -------------------------------------------------------------------------------- /static/img/got-questions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/got-questions.png -------------------------------------------------------------------------------- /static/img/guides-sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/guides-sketch.png -------------------------------------------------------------------------------- /static/img/gutenberg-g-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/img/gutenberg-interface-sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/gutenberg-interface-sketch.png -------------------------------------------------------------------------------- /static/img/gutenberg-interface.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/gutenberg-interface.jpg -------------------------------------------------------------------------------- /static/img/gutenberg-sidebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/gutenberg-sidebar.png -------------------------------------------------------------------------------- /static/img/gutenberg-toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/gutenberg-toolbar.png -------------------------------------------------------------------------------- /static/img/image-block-block-styles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/image-block-block-styles.png -------------------------------------------------------------------------------- /static/img/image-block-styles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/image-block-styles.png -------------------------------------------------------------------------------- /static/img/inner-blocks-core-columns-screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/inner-blocks-core-columns-screenshot.jpg -------------------------------------------------------------------------------- /static/img/inner-blocks-core-group-screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/inner-blocks-core-group-screenshot.jpg -------------------------------------------------------------------------------- /static/img/inner-blocks-one-mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/inner-blocks-one-mockup.png -------------------------------------------------------------------------------- /static/img/inner-blocks-one-scribble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/inner-blocks-one-scribble.png -------------------------------------------------------------------------------- /static/img/inner-blocks-two-mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/inner-blocks-two-mockup.png -------------------------------------------------------------------------------- /static/img/inner-blocks-two-scribble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/inner-blocks-two-scribble.png -------------------------------------------------------------------------------- /static/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/img/pattern-modal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/pattern-modal.png -------------------------------------------------------------------------------- /static/img/pullquote-core-block-style-removed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/pullquote-core-block-style-removed.png -------------------------------------------------------------------------------- /static/img/pullquote-core-block-style.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/pullquote-core-block-style.png -------------------------------------------------------------------------------- /static/img/redux-api-design-simplified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/redux-api-design-simplified.png -------------------------------------------------------------------------------- /static/img/redux-api-design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/redux-api-design.png -------------------------------------------------------------------------------- /static/img/reference-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/reference-icon.png -------------------------------------------------------------------------------- /static/img/reference-sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/reference-sketch.png -------------------------------------------------------------------------------- /static/img/sample-design-boxes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/sample-design-boxes.png -------------------------------------------------------------------------------- /static/img/slotfill-light-dark-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/slotfill-light-dark-mode.png -------------------------------------------------------------------------------- /static/img/spacing-presets-in-use.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/spacing-presets-in-use.gif -------------------------------------------------------------------------------- /static/img/supports-align.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/supports-align.png -------------------------------------------------------------------------------- /static/img/supports-anchor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/supports-anchor.png -------------------------------------------------------------------------------- /static/img/supports-classname.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/supports-classname.png -------------------------------------------------------------------------------- /static/img/supports-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/supports-color.png -------------------------------------------------------------------------------- /static/img/supports-default-style-picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/supports-default-style-picker.png -------------------------------------------------------------------------------- /static/img/supports-dimension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/supports-dimension.png -------------------------------------------------------------------------------- /static/img/supports-edit-html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/supports-edit-html.png -------------------------------------------------------------------------------- /static/img/supports-typography.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/supports-typography.png -------------------------------------------------------------------------------- /static/img/text-format-design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/text-format-design.png -------------------------------------------------------------------------------- /static/img/training-sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/training-sketch.png -------------------------------------------------------------------------------- /static/img/tutorial/docsVersionDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/tutorial/docsVersionDropdown.png -------------------------------------------------------------------------------- /static/img/tutorial/localeDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/tutorial/localeDropdown.png -------------------------------------------------------------------------------- /static/img/variations-block-cta-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/variations-block-cta-1.png -------------------------------------------------------------------------------- /static/img/variations-block-cta-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/variations-block-cta-2.png -------------------------------------------------------------------------------- /static/img/variations-block-next-steps-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/variations-block-next-steps-1.png -------------------------------------------------------------------------------- /static/img/variations-block-next-steps-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/variations-block-next-steps-2.png -------------------------------------------------------------------------------- /static/img/what-to-build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/gutenberg-best-practices/154905280dc73a2741f0aa24266039ed48a6d571/static/img/what-to-build.png -------------------------------------------------------------------------------- /static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /training/Block-Based-Themes/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | sidebar_label: Block Based Themes 4 | description: Introduction to the Block-Based Themes training course. 5 | keywords: [gutenberg, wordpress block editor, training, course, fse, full site editing, block-based-themes, theme.json] 6 | --- 7 | 8 | # 10up Block Based Themes Training 9 | 10 | :::info 11 | This training course is currently in progress. Check back soon for updates. 12 | ::: 13 | 14 | ## Overview 15 | 16 | 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. 17 | -------------------------------------------------------------------------------- /training/Blocks/05-variations.md: -------------------------------------------------------------------------------- 1 | # Lesson 5: Block variations 2 | 3 | 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. 4 | 5 | There is a thin line between when you should use block variations and when you should use block patterns. You often can implement a design as either or with similar effects. 6 | 7 | As a rule of thumb, you can think of the pattern as being more about layout & visual representation whilst variations are more about functionality. 8 | 9 | ## Learning Outcomes 10 | 11 | 1. Learn what block variations are and how to use them 12 | 2. Learn how to register block variations 13 | 14 | ## What are "block variations"? 15 | 16 | [Block Variations](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-variations/) is the API that allows you to create blocks that are _similar_ to existing blocks but have differences that you can define. You could create a variation of the Group block, for example, that has different default values of attributes, pre-defined classnames, and even InnerBlocks. 17 | In WordPress Core, you can find block variations when looking at the embed blocks. Under the hood in the codebase there only is one core embed block. And all the various branded embeds are variations of that one block. 18 | 19 | ![Core Embed block Variations in the Inserter](../../static/img/embed-block-variations-overview.png) 20 | 21 | Another example is the core columns block. WordPress uses variations to allow editors to quickly choose from a predefined list of variations when selecting what column layout you want to get started with. 22 | 23 | ![Core Columns Variation Picker](../../static/img/columns-block-variations-picker.png) 24 | 25 | As you can see from these two examples, the API is quite versatile because it allows you to define the scope for where block variations should be shown. In the case of the Embed blocks, the scope is set to `inserter` because you want editors to be able to choose from the different variants when they look at all the available blocks. 26 | 27 | The Columns block on the other hand sets the scope to `block` and uses the block's initial setup state to allow the editor to choose which variant they'd like to use. 28 | 29 | :::note 30 | The default `scope` is both `block` and `inserter` 31 | ::: 32 | 33 | ## Exercise Overview 34 | 35 | Let's say we have a client that needs to use the columns block in a 4 columns layout very often. By default the block only allows editors to choose between 100, 50/50, 30/70, 70/30, 33/33/33, and 25/50/25 percentage splits for the columns. 36 | 37 | Of course, you could skip the setup and manually configure it to have 4 25% wide columns, but our client needs to use it very often and adding a new option for 25/25/25/25 would be a big time saver for them. 38 | 39 | :::note 40 | If you get stuck, you can take a look at the completed example located in the [`includes/block-variations`](https://github.com/10up/gutenberg-lessons/blob/trunk/themes/tenup-theme/includes/block-variations/four-columns-variation-completed.js) folder of the tenup theme. 41 | ::: 42 | 43 | 1. Create a new file in the `includes/block-variations` folder called `four-columns-equal.js` 44 | 2. import that new file into the `index.js` file inside the same folder 45 | 3. Use the [`registerBlockVariation`](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-variations/) to register a new variant of the core columns block 46 | 47 | ## Takeaways 48 | 49 | Block Variants are a great way to add extensibility to blocks by allowing others to create variants that will show in the initial variant picker. They also allow you to share code between similar blocks like the core embeds ones without having to build many individual blocks. 50 | 51 | ## Further Reading 52 | 53 | * [Block Variations - 10up Gutenberg Best Practices](../../reference/Blocks/block-variations) 54 | * [Block Variations - Block Editor Handbook](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-variations/) 55 | * [How to use block variations in WordPress - CSS Tricks](https://css-tricks.com/how-to-use-block-variations-in-wordpress/) 56 | * [Using Gutenberg Block Variations - Rich Tabor](https://richtabor.com/block-variations/) 57 | * [BlockBook](https://youknowriad.github.io/blockbook/block/) 58 | -------------------------------------------------------------------------------- /training/Blocks/07-rich-text-formats.md: -------------------------------------------------------------------------------- 1 | # Lesson 7: Rich Text Formats 2 | 3 | ## Learning Outcomes 4 | 5 | 1. Learn what RichText Formats are 6 | 2. Understand how you can control them in RichText components 7 | 3. Build your own custom Rich Text Format 8 | 9 | ## What are "Rich Text Formats"? 10 | 11 | Rich Text within WordPress is the underlying engine that powers the text editing capabilities in the new Editor. The way rich text works is by representing the text, whether it is an Element Tree (DOM), an HTML String, or a Plain Text string, as a Rich Text Object. Under the hood that representation looks something like this: 12 | 13 | ```js 14 | { 15 | text: string, 16 | formats: Array, 17 | replacements: Array, 18 | ?start: number, 19 | ?end: number, 20 | } 21 | ``` 22 | 23 | As you can see in this, the actual text is stored separately from the formatting. The formats are ranges within that text, which will get output with custom formatting. So this means that whenever you use a `RichText` component in the editor and select a piece of text to make it Bold or Italic for example you apply a Rich Text Format on that text selection. And the format determines what should happen with that range of text. 24 | 25 | Any format can either have an [HTML tag with text-level semantics](https://www.w3.org/TR/html5/textlevel-semantics.html#text-level-semantics-usage-summary) or a tag and class name to identify the format. 26 | 27 | Core comes with a whole bunch of core Rich Text Formats. They are all created within their package called [`format-library`](https://github.com/WordPress/gutenberg/tree/trunk/packages/format-library/src). 28 | 29 | ![Core Rich Text Formats Dropdown showing "Inline Code" as selected](../../static/img/core-rich-text-formats-screenshot.jpg) 30 | 31 | In the same way, core adds all these core formats you can add your custom formats and also remove the formats that core or another plugin created. 32 | 33 | ## When should you use "Rich Text Formats" 34 | 35 | Rich Text Formats make sense to use whenever you want editors to be able to manipulate something inline. Not on a block level. So the same way you can use the Core "Inline Image" to place a special inline element into your text, you can create your format that adds a special inline element at the current cursor position. Or more commonly you can add a custom format to apply to a range of text to wrap it in a special class name or inline HTML tag. 36 | 37 | ## Exercise Overview 38 | 39 | Let's say we get the following design on a project: 40 | ![Design showing text with some areas being highlighted by a gradient background](../../static/img/text-format-design.png) 41 | The client wants to be able to highlight text across their site with a gradient text color. This should not be limited to specific blocks but be available for any text on the page. 42 | 43 | There is already a starter file for this component that can be found under `themes/tenup-theme/includes/text-formats/gradient-starter.js`. It contains all the imports that we need and what still needs to be done is calling the `registerFormatType` function with the correct information to register our format. 44 | 45 | ## Takeaways 46 | 47 | Rich Text Formats are a great way to add inline formatting controls and therefore a great resource for anything that should not affect an entire block but anything inline. 48 | 49 | Registering a Format works very similarly to registering a block. There are two parameters we need to pass to the `registerFormatType` function. 50 | 51 | 1. Every format needs to have a unique name. The formats are named the same way blocks are named with a `namespace` at the beginning followed by a `/` divider and the `name`. 52 | 2. Every format also needs to have some format options defined. For starters, every format needs to have a `title` and a `tagName`. It optionally can have a `className` and or some inline `styles` and then it must have an `edit` method that defines the actual UI with which we interact. 53 | 54 | In our case, we don't need inline styling because we are only toggling on or off a gradient. So we can _just_ set the `className` to `has-text-gradient` and then our styles will get applied to that element. 55 | 56 | The `edit` method itself is a slot to render things in the Block toolbar in the formatting controls section. So we can directly use the `RichTextToolbarButton` component from the `@wordpress/block-editor` package. As props we get passed whether the format is currently active (`isActive`), the `value` of the format and an `onChange` handler that we need to call whenever we want to change the format. Directly manipulating the `value` of a stored rich text format isn't the best idea though. Formats are stored as complex objects with loads of metadata. 57 | 58 | Luckily Core has created helper functions that can be imported from the `@wordpress/rich-text` package that allow you to more easily and safely manipulate the formats. In our case we only want to toggle the format and therefore can use the `toggleFormat` function. 59 | 60 | In order to pass the information to the `onChange` function we get from the rich text format we need to do the following: 61 | 62 | ```js 63 | onChange( 64 | toggleFormat(value, { type: FORMAT_NAME }) 65 | ); 66 | ``` 67 | 68 | We need to wrap that in a function and then assign that to the `onClick` event of the `RichTextToolbarButton`. 69 | 70 | Now all that is left is adding a proper icon and a title to the `RichTextToolbarButton` and passing the `isActive` value to the `isActive` prop and we are done. 71 | 72 | ## Next steps 73 | 74 | 1. Take a look at a more complex rich text format like the "text-color" format from core: [Text Color Rich Text Format](https://github.com/WordPress/gutenberg/blob/trunk/packages/format-library/src/text-color/index.js) 75 | 2. Try to change our simple gradient toggle to allow editors to modify the gradient or pick from a predefined list of gradients in a popover like core does in the "text-color" format. 76 | 77 | ## Further reading 78 | 79 | - [Block Editor Handbook: Introduction to the Format API](https://developer.wordpress.org/block-editor/how-to-guides/format-api/) 80 | - [How to Create Custom Text Formats for Gutenberg Block Editor - by Jeffrey Carandang](https://jeffreycarandang.com/how-to-create-custom-text-formats-for-gutenberg-block-editor/) 81 | -------------------------------------------------------------------------------- /training/Blocks/08-slot-fill.md: -------------------------------------------------------------------------------- 1 | # Lesson 8: SlotFill 2 | 3 | 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/) 4 | 5 |
6 | 7 | For a much deeper dive into the entire SlotFill system and how it works internally, watch this presentation from the 2019 JavaScript for WordPress conference. 8 | 9 | 10 |

11 | 12 |

13 | 14 |
15 | 16 | ## Lesson Outcomes 17 | 18 | 1. Understanding of the SlotFill system in Gutenberg. 19 | 2. Create and register a plugin to add a new Fill to the Document Settings panel. 20 | 3. How to register post meta to be used in Gutenberg. 21 | 4. Work with `useSelect` and `useDispatch` to retrieve and set custom post meta. 22 | 5. Add the `ToggleControl` component to display and update the meta value. 23 | 6. Filter the `body_class` to display the new class on the frontend. 24 | 25 | ## Overview 26 | 27 | Imagine you're building a site that needs to support per-post [light/dark mode](https://css-tricks.com/a-complete-guide-to-dark-mode-on-the-web/). On each post, the editor can decide whether the post will have a dark background or a light one on the frontend. 28 | 29 | We'll give the editor this control via a toggle in a Slot in the Inspector. In the template, we'll see if this toggle is checked or not and assign a corresponding class to the body tag: `` or ``. *We can worry about the CSS details required to make this happen in another lesson.* 30 | 31 | Here's the result in the editor: 32 | 33 | ![alt text](../../static/img/slotfill-light-dark-mode.png "Light or dark mode") 34 | 35 | ## Tasks 36 | 37 | ### 1. Registering a plugin 38 | 39 | 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`. 40 | 41 | ### 2. Prepare the post meta for the REST API 42 | 43 | In order to access or save post meta in Gutenberg, the meta needs to be exposed to the REST API. For the lesson, this has already been done in [includes/core.php](https://github.com/10up/gutenberg-lessons/blob/trunk/themes/tenup-theme/includes/core.php#L37) but this is a very important step as, without it, meta will not be saved to the database. 44 | 45 | ### 3. Add the ToggleControl component 46 | 47 | 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. 48 | 49 | :::warning 50 | **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.** 51 | ::: 52 | 53 | ### 4. Display the class on the frontend 54 | 55 | We can filter the `body_class` function to display this new meta as class a filter. This has been done already but have a look at it in [includes/core.php](https://github.com/10up/gutenberg-lessons/tree/trunk/themes/tenup-theme/includes/core.php#L63) 56 | 57 | ### 5. Add another control 58 | 59 | We now have two modes for our posts: light and dark. Imagine your client comes back and says, "We want to be able to select from 4 different modes/themes - Light, Dark, Milkshake and Popsicle." Swap the current toggle for a select dropdown with 4 options for the theme and output the appropriate body class on the frontend: ``, ``, ``, ``. 60 | -------------------------------------------------------------------------------- /training/Blocks/09-build-your-own.md: -------------------------------------------------------------------------------- 1 | # Lesson 9: Build Your Own 2 | 3 | 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. 4 | 5 | ## What to Build 6 | 7 | Your task is to build a block called "Author Byline". The block should show the author of the current post next to their avatar image. There should be a block setting for hiding the avatar image. 8 | 9 | To start you should make a clone of the [10up WP Scaffold](https://github.com/10up/wp-scaffold). The block should be created as a part of the theme. 10 | 11 | ## Technical Specifications 12 | 13 | * Built as a dynamic block 14 | * Following 10up Best Practices 15 | * Follows block building patterns shown in the [10up WP Scaffold example block](https://github.com/10up/wp-scaffold/tree/trunk/themes/10up-theme/includes/blocks/example-block). 16 | -------------------------------------------------------------------------------- /training/Blocks/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | sidebar_label: Blocks 4 | description: Introduction to the Blocks training course. 5 | keywords: [gutenberg, wordpress block editor, training, course] 6 | --- 7 | 8 | # 10up Gutenberg Training 9 | 10 | 11 | 12 | ## Overview 13 | 14 | The purpose of this project is to help you build and customize (Gutenberg) blocks. This training applies to all engineering disciplines at 10up. 15 | 16 | ## Training Prerequisites 17 | 18 | * Basic understanding of WordPress including how themes are structured. 19 | * Understanding of JavaScript fundamentals and ES6+ concepts. 20 | * Basic understanding of React components. 21 | 22 | For resources on learning JavaScript and React.js, look through this [internal document](https://internal.10up.com/docs/javascript-tutorials/). 23 | 24 | ## Project Setup 25 | 26 | For this training, we recommend the usage of [10up-docker](https://github.com/10up/wp-local-docker-v2) for the local environment. To get everything setup on your computer follow these steps here: 27 | 28 | 1. Create a local WordPress installation with the domain `gutenberg-training.test` 29 | 30 | ```bash 31 | 10updocker create gutenberg-training 32 | ``` 33 | 34 | Follow the prompts within the terminal 35 | 36 | ```bash 37 | ? What is the primary hostname for your site? (Ex: docker.test) gutenberg-training.test 38 | ? Are there additional domains the site should respond to? No 39 | ? What version of PHP would you like to use? 8.0 40 | ? Do you want to install WordPress? Yes 41 | ? Select a WordPress installation type: Single Site 42 | ? Site Name: gutenberg-training.test 43 | ? Admin Username: admin 44 | ? Admin Password: password 45 | ? Admin Email: admin@example.com 46 | ? Do you want to remove the default content? No 47 | ? Do you want to set a proxy for media assets? (i.e. Serving /uploads/ directory assets 48 | from a production site): No 49 | ? Do you need Elasticsearch: No 50 | ``` 51 | 52 | 2. Move into the `gutenber-training-test` directory and clone the [`gutenberg-lessons`](https://github.com/10up/gutenberg-lessons) repository into the `wordpress` directory replacing the `wp-content` folder 53 | 54 | If not already in the `gutenberg-training-test` directory: 55 | 56 | ```bash 57 | cd ~/wp-local-docker-sites/gutenberg-training-test/ 58 | ``` 59 | 60 | Then run: 61 | 62 | ```bash 63 | cd wordpress && rm -rf wp-content && git clone git@github.com:10up/gutenberg-lessons.git wp-content 64 | ``` 65 | 66 | 3. Install the dependencies and build the assets 67 | 68 | ```bash 69 | cd wp-content && npm install && npm run build 70 | ``` 71 | 72 | 4. Activate the tenup-theme in WordPress 73 | 74 | ```bash 75 | 10updocker wp theme activate tenup-theme 76 | ``` 77 | 78 | 5. Navigate to your site [gutenberg-training.test](https://gutenberg-training.test/wp-admin) and view some of the sample blocks and patterns that have already been created within the Block Picker 79 | 80 | * To access the Block Picker click on a Page or a Post and click the "+" button to display all available blocks. At the bottom of the picker there is a row named "Completed Blocks" those are the custom blocks created. At the top of the picker clicking on "Patterns" will show the custom patterns that have been created. 81 | 82 | :::caution 83 | The `tenup-theme` build system requires node version **16** in order to build successfully. If you have [`nvm`](https://github.com/nvm-sh/nvm) installed it should auto-detect which version to use. 84 | 85 | Also, make sure that you are running your npm commands from the `wp-content` folder. Not from the Theme folder. 86 | ::: 87 | 88 | If you want to have your code automatically compile again and even hot reload directly in the editor when you make any changes you can start the watch mode. Before that works you need to enable the [WordPress Debug](https://wordpress.org/support/article/debugging-in-wordpress/) mode by setting `WP_DEBUG` and `SCRIPT_DEBUG` in your `wp-config.php` file to true. 89 | 90 | Once that is done you can start the watch mode by running: 91 | 92 | ```bash 93 | npm run watch 94 | ``` 95 | 96 | ## The block setup in 10up's wp-scaffold 97 | 98 | The 10up `wp-scaffold` theme includes a few helpers that make working with custom blocks easier. For example the block registration happens automatically when you create a new folder with a `block.json` file in it inside the `includes/blocks/` directory of the theme. 99 | 100 | The registration also takes any block assets that are defined in the `block.json` file (`editorScript`, `viewScript`, `script`, `style`, and `editorStyle`) and automatically adds them to the list of entrypoints that get transpiled using [10up-toolkit](https://www.npmjs.com/package/10up-toolkit). 101 | 102 | If there is a `markup.php` file located inside the same directory as the `block.json`, it will automatically get added as the `render_callback` and therefore provide a quick way to build dynamic blocks. 103 | 104 | ## Lessons 105 | 106 | * [Lesson 1: Anatomy of a block](./01-overview.md) 107 | * [Lesson 2: A Simple CTA block](./02-cta-lesson.md) 108 | * [Lesson 3: Block Styles](./03-styles.md) 109 | * [Lesson 4: Block Patterns](./04-patterns.md) 110 | * [Lesson 5: Block Variations](./05-variations.md) 111 | * [Lesson 6: Inner Blocks / Block Nesting](./06-inner-blocks.md) 112 | * [Lesson 7: Rich Text Formats](./07-rich-text-formats.md) 113 | * [Lesson 8: Slot Fill](./08-slot-fill.md) 114 | * [Lesson 9: Build your own](./09-build-your-own.md) 115 | 116 | ## Support 117 | 118 | 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). 119 | -------------------------------------------------------------------------------- /training/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 0 3 | sidebar_label: Introduction 4 | description: Introduction to the various training courses. 5 | keywords: [gutenberg, wordpress block editor, training, course, fse, full site editing, block-based-themes, theme.json] 6 | --- 7 | 8 | # 10up WordPress Training 9 | 10 | ## Overview 11 | 12 | On this page, you will find a collection of training courses designed to help you build and customize WordPress themes and blocks. 13 | 14 | ## Courses 15 | 16 | - [Building Blocks](/training/Blocks/) 17 | - [Building Block-based Themes](/training/Block-Based-Themes/) _(in progress)_ 18 | --------------------------------------------------------------------------------